mcp-crypto-toolkit 1.2.3 → 1.3.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/CHANGELOG.md +44 -1
- package/README.md +103 -6
- package/dist/http.js +2 -1
- package/dist/index.js +1 -1
- package/package.json +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.0] - 2026-09-04
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Cloudflare Workers deploy (`src/worker.ts`, `wrangler.toml`, `npm run deploy:cf`)
|
|
15
|
+
- Hosted remote MCP: `https://mcp-crypto-toolkit.nad33mahm3d.workers.dev/mcp`
|
|
16
|
+
- `trending` tool — CoinGecko trending coins + Fear & Greed Index
|
|
17
|
+
- `whats-trending` prompt; market-overview also calls trending
|
|
18
|
+
- Worker per-IP rate limit (default 60 req/min) and optional `MCP_API_KEY` gate
|
|
19
|
+
- GitHub Action to deploy Worker on release (`deploy-cf.yml`)
|
|
20
|
+
- README: live connector URL, DeepWiki, mcpservers.org badge, Claude connector screenshot
|
|
21
|
+
- `server.json` `remotes` entry for the hosted endpoint
|
|
22
|
+
|
|
23
|
+
## [1.2.7] - 2026-09-04
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Align npm OIDC publish with working trusted-publisher setup: GitHub `environment: release`, `registry-url`, `npm@^11.5.1`, `--provenance`
|
|
28
|
+
|
|
29
|
+
## [1.2.6] - 2026-09-04
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- OIDC publish: remove `registry-url` from setup-node so npm uses trusted publishing instead of falling back to missing-token 404
|
|
34
|
+
|
|
35
|
+
## [1.2.5] - 2026-09-04
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- npm publish uses OIDC trusted publishing (no `NPM_TOKEN`); Node 24 in publish workflow for npm 11.5.1+
|
|
40
|
+
|
|
41
|
+
## [1.2.4] - 2026-09-04
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- HTTP transport: answer JSON-RPC notifications (e.g. `notifications/initialized`) with a non-empty 202 so reverse proxies (Cloudflare / MCP Hosting) do not hang — fixes Claude.ai Custom Connector connected with 0 tools
|
|
46
|
+
- Tool JSON Schemas: add missing `type` for `historical_price.days` and `compare_coins.coins`
|
|
47
|
+
- CORS `OPTIONS` preflight on `/mcp`
|
|
48
|
+
|
|
10
49
|
## [1.2.3] - 2026-09-03
|
|
11
50
|
|
|
12
51
|
### Added
|
|
@@ -66,7 +105,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
66
105
|
- Binance P2P PKR path for conversions
|
|
67
106
|
- MIT license and README
|
|
68
107
|
|
|
69
|
-
[Unreleased]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.
|
|
108
|
+
[Unreleased]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.7...HEAD
|
|
109
|
+
[1.2.7]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.6...v1.2.7
|
|
110
|
+
[1.2.6]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.5...v1.2.6
|
|
111
|
+
[1.2.5]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.4...v1.2.5
|
|
112
|
+
[1.2.4]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.3...v1.2.4
|
|
70
113
|
[1.2.3]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.2...v1.2.3
|
|
71
114
|
[1.2.2]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.1...v1.2.2
|
|
72
115
|
[1.2.1]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.2.0...v1.2.1
|
package/README.md
CHANGED
|
@@ -4,13 +4,20 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://modelcontextprotocol.io)
|
|
6
6
|
[](https://github.com/nad33mahm3d/mcp-crypto-toolkit/actions/workflows/ci.yml)
|
|
7
|
+
[](https://mcpservers.org/servers/nad33mahm3d/mcp-crypto-toolkit)
|
|
8
|
+
[](https://deepwiki.com/nad33mahm3d/mcp-crypto-toolkit)
|
|
7
9
|
|
|
8
10
|
**Live crypto prices, conversion, gas tracker, portfolio tools, and calculators for AI agents.**
|
|
9
11
|
|
|
10
12
|
Ask Claude: *What's ETH doing? Top 10 coins? Gas on Base? What's my portfolio worth?*
|
|
11
13
|
|
|
14
|
+
**Hosted (Claude.ai Custom Connector):** [`https://mcp-crypto-toolkit.nad33mahm3d.workers.dev/mcp`](https://mcp-crypto-toolkit.nad33mahm3d.workers.dev/mcp)
|
|
15
|
+
**Docs:** [DeepWiki](https://deepwiki.com/nad33mahm3d/mcp-crypto-toolkit) · [mcpservers.org](https://mcpservers.org/servers/nad33mahm3d/mcp-crypto-toolkit)
|
|
16
|
+
|
|
12
17
|

|
|
13
18
|
|
|
19
|
+

|
|
20
|
+
|
|
14
21
|
## Features
|
|
15
22
|
|
|
16
23
|
- Live prices with market cap, volume, rank, ATH/ATL (CoinGecko)
|
|
@@ -22,9 +29,10 @@ Ask Claude: *What's ETH doing? Top 10 coins? Gas on Base? What's my portfolio wo
|
|
|
22
29
|
- Historical point-in-time + range charts (7d/30d/…)
|
|
23
30
|
- EVM gas tracker with USD transfer estimates
|
|
24
31
|
- Profit/loss calculator with exchange + network fees
|
|
32
|
+
- Trending coins + Crypto Fear & Greed Index
|
|
25
33
|
- MCP prompts for common workflows
|
|
26
34
|
- **Zero config** — no API key required (optional `COINGECKO_API_KEY` for higher limits)
|
|
27
|
-
- stdio + HTTP transports
|
|
35
|
+
- stdio + HTTP + Cloudflare Workers transports
|
|
28
36
|
|
|
29
37
|
## Quick Install
|
|
30
38
|
|
|
@@ -68,7 +76,88 @@ npm run start:http
|
|
|
68
76
|
# POST http://localhost:3000/mcp
|
|
69
77
|
```
|
|
70
78
|
|
|
71
|
-
|
|
79
|
+
#### Deploy (Smithery / remote MCP) — optional
|
|
80
|
+
|
|
81
|
+
**You do not need paid hosting for discoverability.** This server already installs via `npx -y mcp-crypto-toolkit` (stdio) and is listed on the [Official MCP Registry](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.nad33mahm3d/mcp-crypto-toolkit). Glama and PulseMCP ingest from there — no Fly/Railway bill required.
|
|
82
|
+
|
|
83
|
+
Only deploy HTTP if you want a **remote** `/mcp` endpoint (e.g. Claude.ai Custom Connector or Smithery URL publish).
|
|
84
|
+
|
|
85
|
+
#### Claude.ai Custom Connector
|
|
86
|
+
|
|
87
|
+
Public hosted endpoint (Cloudflare Workers):
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
https://mcp-crypto-toolkit.nad33mahm3d.workers.dev/mcp
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
1. In Claude.ai: **Settings → Connectors → Add custom connector**
|
|
94
|
+
2. Paste the URL above (must include `/mcp`)
|
|
95
|
+
3. You should see **10 tools** (get price, convert, trending, …)
|
|
96
|
+
|
|
97
|
+
Or deploy your own (Cloudflare / Docker / Render) and use `https://YOUR-HOST/mcp`. After upgrading a hosted server, remove and re-add the connector if tools still show as empty.
|
|
98
|
+
|
|
99
|
+
Do **not** put a remote URL in `claude_desktop_config.json` — that file is stdio-only. For Claude Desktop local use, prefer:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"crypto-toolkit": {
|
|
105
|
+
"command": "npx",
|
|
106
|
+
"args": ["-y", "mcp-crypto-toolkit"]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Cloudflare Workers (recommended remote — free tier)**
|
|
113
|
+
|
|
114
|
+
Uses the web-standard MCP transport (`src/worker.ts`) — no Docker required.
|
|
115
|
+
|
|
116
|
+
Live demo: [health](https://mcp-crypto-toolkit.nad33mahm3d.workers.dev/health) · [MCP](https://mcp-crypto-toolkit.nad33mahm3d.workers.dev/mcp)
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm install
|
|
120
|
+
npx wrangler login
|
|
121
|
+
npm run deploy:cf
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
After deploy: `https://mcp-crypto-toolkit.<your-subdomain>.workers.dev/health`
|
|
125
|
+
Claude / Smithery URL: `https://mcp-crypto-toolkit.<your-subdomain>.workers.dev/mcp`
|
|
126
|
+
|
|
127
|
+
Local preview: `npm run dev:cf`
|
|
128
|
+
|
|
129
|
+
Optional secrets / vars:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npx wrangler secret put COINGECKO_API_KEY
|
|
133
|
+
npx wrangler secret put MCP_API_KEY # optional: require Bearer / x-api-key on /mcp
|
|
134
|
+
# Pro keys also need: wrangler.toml [vars] COINGECKO_PRO = "1"
|
|
135
|
+
# Optional: RATE_LIMIT_PER_MINUTE = "60" (default)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
CI: add repo secrets `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` — Worker auto-deploys on GitHub Release (and via workflow_dispatch).
|
|
139
|
+
|
|
140
|
+
**Render (free — no credit card)**
|
|
141
|
+
|
|
142
|
+
1. Fork/connect repo at [render.com](https://render.com)
|
|
143
|
+
2. New → Blueprint → point at this repo (`render.yaml` uses the **Free** plan)
|
|
144
|
+
3. After deploy: `https://YOUR-SERVICE.onrender.com/health`
|
|
145
|
+
4. Smithery: publish `https://YOUR-SERVICE.onrender.com/mcp`
|
|
146
|
+
|
|
147
|
+
Free tier sleeps after ~15 min idle; first request may take ~30s to wake.
|
|
148
|
+
|
|
149
|
+
**Docker (local / your own VPS)**
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
docker build -t mcp-crypto-toolkit .
|
|
153
|
+
docker run -p 3000:3000 mcp-crypto-toolkit
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Paid PaaS (if you outgrow free tier):** Fly.io and Railway work with the included `fly.toml` / `railway.toml` but are **not free** long-term.
|
|
157
|
+
|
|
158
|
+
Optional env: `COINGECKO_API_KEY`, `COINGECKO_PRO=1`, gas oracle keys (see table below).
|
|
159
|
+
|
|
160
|
+
## Tools (10)
|
|
72
161
|
|
|
73
162
|
| Tool | Description |
|
|
74
163
|
|------|-------------|
|
|
@@ -81,12 +170,14 @@ npm run start:http
|
|
|
81
170
|
| `historical_price` | Point date or range chart + investment snapshot |
|
|
82
171
|
| `gas_tracker` | Gas fees + estimated transfer cost (USD) |
|
|
83
172
|
| `profit_calc` | P&L, ROI, break-even (fees + network fee) |
|
|
173
|
+
| `trending` | Trending coins + Fear & Greed Index |
|
|
84
174
|
|
|
85
175
|
## Prompts
|
|
86
176
|
|
|
87
177
|
| Prompt | Purpose |
|
|
88
178
|
|--------|---------|
|
|
89
|
-
| `market-overview` | Top coins + market tone |
|
|
179
|
+
| `market-overview` | Top coins + trending + market tone |
|
|
180
|
+
| `whats-trending` | Trending coins + Fear & Greed |
|
|
90
181
|
| `analyze-coin` | Price + 7d trend for one coin |
|
|
91
182
|
| `trade-pnl` | Walk through a P&L calc |
|
|
92
183
|
| `gas-check` | Gas + transfer cost advice |
|
|
@@ -102,6 +193,7 @@ npm run start:http
|
|
|
102
193
|
7. *Show SOL's 30-day price range*
|
|
103
194
|
8. *What's gas on Base right now in USD?*
|
|
104
195
|
9. *I bought at 100, sold at 120, qty 10, 0.1% fees — profit?*
|
|
196
|
+
10. *What's trending and what's the Fear & Greed index?*
|
|
105
197
|
|
|
106
198
|
## Development
|
|
107
199
|
|
|
@@ -120,6 +212,8 @@ npm run inspector
|
|
|
120
212
|
|----------|---------|
|
|
121
213
|
| `COINGECKO_API_KEY` | Higher CoinGecko rate limits (still optional) |
|
|
122
214
|
| `COINGECKO_PRO=1` | Use Pro header with your key |
|
|
215
|
+
| `MCP_API_KEY` | Worker only: require Bearer / `x-api-key` on `/mcp` |
|
|
216
|
+
| `RATE_LIMIT_PER_MINUTE` | Worker only: default `60` |
|
|
123
217
|
| `ETHERSCAN_API_KEY` / `BSCSCAN_API_KEY` / … | Better gas oracles |
|
|
124
218
|
| `OPTIMISM_API_KEY` | Optimism gas oracle |
|
|
125
219
|
| `PORT` | HTTP server port (default 3000) |
|
|
@@ -134,17 +228,20 @@ See [CONTRIBUTING.md](https://github.com/nad33mahm3d/mcp-crypto-toolkit/blob/mai
|
|
|
134
228
|
- [Security Policy](https://github.com/nad33mahm3d/mcp-crypto-toolkit/blob/main/SECURITY.md)
|
|
135
229
|
- [Support](https://github.com/nad33mahm3d/mcp-crypto-toolkit/blob/main/SUPPORT.md)
|
|
136
230
|
- [Changelog](https://github.com/nad33mahm3d/mcp-crypto-toolkit/blob/main/CHANGELOG.md)
|
|
231
|
+
- [DeepWiki](https://deepwiki.com/nad33mahm3d/mcp-crypto-toolkit) — auto-generated architecture docs
|
|
232
|
+
- [mcpservers.org](https://mcpservers.org/servers/nad33mahm3d/mcp-crypto-toolkit)
|
|
137
233
|
|
|
138
234
|
### Releasing (maintainers)
|
|
139
235
|
|
|
140
|
-
1.
|
|
236
|
+
1. One-time: npm package **Settings → Trusted Publisher** → GitHub Actions → repo `nad33mahm3d/mcp-crypto-toolkit`, workflow `publish.yml`, environment **`release`**, allow **`npm publish`**
|
|
141
237
|
2. Update `CHANGELOG.md`
|
|
142
238
|
3. Create a GitHub Release with tag `vX.Y.Z`
|
|
143
|
-
4. Actions publishes to npm
|
|
239
|
+
4. Actions publishes to npm via OIDC (no `NPM_TOKEN`; provenance automatic)
|
|
144
240
|
|
|
145
241
|
## API Credits
|
|
146
242
|
|
|
147
|
-
- [CoinGecko](https://www.coingecko.com/) — prices
|
|
243
|
+
- [CoinGecko](https://www.coingecko.com/) — prices, markets, trending (free tier, no key required)
|
|
244
|
+
- [Alternative.me](https://alternative.me/crypto/fear-and-greed-index/) — Fear & Greed Index
|
|
148
245
|
- [Binance P2P](https://p2p.binance.com/) — USDT/PKR when converting to PKR
|
|
149
246
|
- [Etherscan](https://etherscan.io/) family — optional gas oracles
|
|
150
247
|
- [Blocknative](https://www.blocknative.com/) — gas fallback
|
package/dist/http.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{createServer as Ye}from"http";import{Server as Be}from"@modelcontextprotocol/sdk/server/index.js";import{StreamableHTTPServerTransport as Ve}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{CallToolRequestSchema as Me,ListToolsRequestSchema as ze,ListPromptsRequestSchema as Le,GetPromptRequestSchema as Ue}from"@modelcontextprotocol/sdk/types.js";var Q={btc:"bitcoin",bitcoin:"bitcoin",eth:"ethereum",ethereum:"ethereum",usdt:"tether",tether:"tether",bnb:"binancecoin",binancecoin:"binancecoin",sol:"solana",solana:"solana",xrp:"ripple",ripple:"ripple",usdc:"usd-coin","usd-coin":"usd-coin",ada:"cardano",cardano:"cardano",avax:"avalanche-2",avalanche:"avalanche-2",doge:"dogecoin",dogecoin:"dogecoin",dot:"polkadot",polkadot:"polkadot",trx:"tron",tron:"tron",link:"chainlink",chainlink:"chainlink",matic:"matic-network",polygon:"matic-network","matic-network":"matic-network",shib:"shiba-inu","shiba-inu":"shiba-inu",ltc:"litecoin",litecoin:"litecoin",bch:"bitcoin-cash","bitcoin-cash":"bitcoin-cash",uni:"uniswap",uniswap:"uniswap",atom:"cosmos",cosmos:"cosmos",xlm:"stellar",stellar:"stellar",xmr:"monero",monero:"monero",etc:"ethereum-classic","ethereum-classic":"ethereum-classic",fil:"filecoin",filecoin:"filecoin",apt:"aptos",aptos:"aptos",arb:"arbitrum",arbitrum:"arbitrum",op:"optimism",optimism:"optimism",near:"near",vet:"vechain",vechain:"vechain",icp:"internet-computer",hbar:"hedera-hashgraph",algo:"algorand",algorand:"algorand",qnt:"quant-network",eos:"eos",aave:"aave",grt:"the-graph",sand:"the-sandbox",mana:"decentraland",axs:"axie-infinity",theta:"theta-token",ftm:"fantom",fantom:"fantom",xtz:"tezos",tezos:"tezos",rune:"thorchain",snx:"havven",crv:"curve-dao-token",mkr:"maker",maker:"maker",comp:"compound-governance-token",ldo:"lido-dao",sui:"sui",sei:"sei-network",inj:"injective-protocol",tia:"celestia",pepe:"pepe",wif:"dogwifcoin",bonk:"bonk",floki:"floki",render:"render-token",rndr:"render-token",fet:"fetch-ai",imx:"immutable-x",gala:"gala",enj:"enjincoin",bat:"basic-attention-token",zec:"zcash",dash:"dash",neo:"neo",kcs:"kucoin-shares",okb:"okb",ton:"the-open-network",kas:"kaspa",stx:"blockstack",rpl:"rocket-pool",blur:"blur",pendle:"pendle",jup:"jupiter-exchange-solana",pyth:"pyth-network",wld:"worldcoin-wld",strk:"starknet",ethfi:"ether-fi",ena:"ethena",ondo:"ondo-finance",not:"notcoin",bome:"book-of-meme",ar:"arweave",flow:"flow",mina:"mina-protocol",ro:"ronin",ronin:"ronin",egld:"elrond-erd-2",egld_m:"elrond-erd-2",cake:"pancakeswap-token","1inch":"1inch",ens:"ethereum-name-service",chz:"chiliz",hot:"holotoken",zil:"zilliqa",icx:"icon",waves:"waves",kava:"kava",celo:"celo",rose:"oasis-network",one:"harmony",iota:"iota",xem:"nem",sc:"siacoin",dcr:"decred",rvn:"ravencoin",zen:"zencash",ont:"ontology",qtum:"qtum",btt:"bittorrent",hnt:"helium",lrc:"loopring",ankr:"ankr",omg:"omisego",storj:"storj",skl:"skale",celr:"celer-network",coti:"coti",ren:"republic-protocol",band:"band-protocol",ocean:"ocean-protocol",api3:"api3",nmr:"numeraire",bal:"balancer",yfi:"yearn-finance",sushi:"sushi",dydx:"dydx-chain",gmx:"gmx",magic:"magic",rdnt:"radiant-capital",joe:"joe",woo:"woo-network",perp:"perpetual-protocol",lqty:"liquity",cvx:"convex-finance",frax:"frax",fxn:"fxn",usdd:"usdd",tusd:"true-usd",dai:"dai",frax_usd:"frax"};var X={eth:1,bnb:56,polygon:137,arbitrum:42161,base:8453,optimism:10},Z={eth:"ethereum",bnb:"binancecoin",polygon:"matic-network",arbitrum:"ethereum",base:"ethereum",optimism:"ethereum"},K=21e3,ee={eth:{url:"https://api.etherscan.io/api",apiKeyEnv:"ETHERSCAN_API_KEY"},bnb:{url:"https://api.bscscan.com/api",apiKeyEnv:"BSCSCAN_API_KEY"},polygon:{url:"https://api.polygonscan.com/api",apiKeyEnv:"POLYGONSCAN_API_KEY"},arbitrum:{url:"https://api.arbiscan.io/api",apiKeyEnv:"ARBISCAN_API_KEY"},base:{url:"https://api.basescan.org/api",apiKeyEnv:"BASESCAN_API_KEY"},optimism:{url:"https://api-optimistic.etherscan.io/api",apiKeyEnv:"OPTIMISM_API_KEY"}},R="mcp-crypto-toolkit/1.0.0 (https://github.com/nad33mahm3d/mcp-crypto-toolkit)";var E=class{store=new Map;timers=new Map;get(e){let o=this.store.get(e);if(o){if(Date.now()>o.expiresAt){this.delete(e);return}return o.value}}set(e,o,a){let n=this.timers.get(e);n&&clearTimeout(n),this.store.set(e,{value:o,expiresAt:Date.now()+a});let r=setTimeout(()=>{this.delete(e)},a);r.unref?.(),this.timers.set(e,r)}clear(){for(let e of this.timers.values())clearTimeout(e);this.timers.clear(),this.store.clear()}delete(e){let o=this.timers.get(e);o&&(clearTimeout(o),this.timers.delete(e)),this.store.delete(e)}},I=new E,H=new E,N=new E,D=new E,k={PRICE:6e4,GAS:15e3,HISTORICAL:36e5,PKR:3e5,SEARCH:12e4};var G="https://api.coingecko.com/api/v3",b=class extends Error{constructor(o,a,n=!1){super(o);this.status=a;this.retryable=n;this.name="CoinGeckoError"}status;retryable};function f(t){let e=t.toLowerCase().trim();return Q[e]??e}function Ie(){let t=process.env.COINGECKO_API_KEY?.trim();return t?t.startsWith("CG-")&&process.env.COINGECKO_PRO==="1"?{"x-cg-pro-api-key":t}:{"x-cg-demo-api-key":t}:{}}async function q(t,e={},o=3){let a;for(let n=0;n<o;n++)try{let r=await fetch(t,{...e,headers:{Accept:"application/json","User-Agent":R,...Ie(),...e.headers??{}}});if(r.status===429){let i=Math.min(1e3*2**n,1e4);if(n<o-1){await te(i);continue}throw new b("CoinGecko rate limit exceeded (429). Wait a few seconds, or set COINGECKO_API_KEY for higher limits.",429,!0)}if(!r.ok)throw new b(`CoinGecko API error: ${r.status} ${r.statusText}`,r.status,r.status>=500);return r}catch(r){if(r instanceof b)throw r;a=r instanceof Error?r:new Error(String(r)),n<o-1&&await te(1e3*2**n)}throw a??new Error("Fetch failed after retries")}async function P(t,e){let o=[...new Set(t.map(f))],a=[...new Set(e.map(p=>p.toLowerCase()))],n=`simple:${o.sort().join(",")}:${a.sort().join(",")}`,r=I.get(n);if(r)return r;let i=new URLSearchParams({ids:o.join(","),vs_currencies:a.join(","),include_24hr_change:"true",include_last_updated_at:"true"}),u=await(await q(`${G}/simple/price?${i}`)).json();return I.set(n,u,k.PRICE),u}async function S(t,e){let o=f(t),a=e.toLowerCase(),r=(await P([o],[a]))[o];if(!r)throw new b(`Coin not found: ${t} (id: ${o})`);let i=r[a];if(i===void 0)throw new b(`Currency not supported: ${e}`);let c=`${a}_24h_change`,u="last_updated_at";return{id:o,price:Number(i),change24h:r[c]!==void 0?Number(r[c]):void 0,lastUpdated:r[u]!==void 0?new Date(Number(r[u])*1e3).toISOString():new Date().toISOString()}}async function j(t,e={}){let o=t.toLowerCase(),a=e.perPage??10,n=e.page??1,r=e.order??"market_cap_desc",i=e.ids?.map(f),c=`markets:${o}:${i?.sort().join(",")??"all"}:${a}:${n}:${r}`,u=I.get(c);if(u)return u;let p=new URLSearchParams({vs_currency:o,order:r,per_page:String(a),page:String(n),sparkline:"false",price_change_percentage:"24h"});i?.length&&p.set("ids",i.join(","));let l=await(await q(`${G}/coins/markets?${p}`)).json();return I.set(c,l,k.PRICE),l}async function Y(t,e){let o=f(t),n=(await j(e,{ids:[o],perPage:1}))[0];if(!n)throw new b(`Coin not found: ${t} (id: ${o})`);return n}async function re(t,e=10){let o=t.trim();if(!o)return[];let a=`search:${o.toLowerCase()}:${e}`,n=I.get(a);if(n)return n;let c=((await(await q(`${G}/search?query=${encodeURIComponent(o)}`)).json()).coins??[]).slice(0,e).map(u=>({id:u.id,name:u.name,symbol:u.symbol,market_cap_rank:u.market_cap_rank}));return I.set(a,c,k.PRICE),c}async function ne(t,e,o){let a=f(t),n=o.toLowerCase(),r=`hist:${a}:${e}:${n}`,i=N.get(r);if(i)return i;let u=await(await q(`${G}/coins/${a}/history?date=${encodeURIComponent(e)}&localization=false`)).json();if(u.error||!u.market_data?.current_price?.[n])throw new b(u.error??`Historical price not found for ${t} on ${e}`);let p={id:a,date:e,price:u.market_data.current_price[n],vsCurrency:n};return N.set(r,p,k.HISTORICAL),p}async function oe(t,e,o){let a=f(t),n=e.toLowerCase(),r=String(o),i=`chart:${a}:${n}:${r}`,c=N.get(i);if(c)return c;let u=new URLSearchParams({vs_currency:n,days:r}),l=((await(await q(`${G}/coins/${a}/market_chart?${u}`)).json()).prices??[]).map(([v,h])=>({timestamp:v,price:h})),d={id:a,vs:n,days:r,prices:l};return N.set(i,d,k.HISTORICAL),d}async function ae(t,e){return(await S(t,e)).price}function te(t){return new Promise(e=>setTimeout(e,t))}function s(t,e=2){return Number.isFinite(t)?Number(t.toFixed(e)):0}function _(t,e=2){return Number.isFinite(t)?Number(t.toFixed(e)):0}function g(t,e){return t==="btc"||t==="eth"?8:e>=1?2:e>=.01?4:8}import{z as B}from"zod";async function O(){let t=D.get("usdt-pkr");if(t)return t;try{let o={rate:await Ae(),source:"binance_p2p",lastUpdated:new Date().toISOString()};return D.set("usdt-pkr",o,k.PKR),o}catch{let o={rate:await Ee(),source:"coingecko",lastUpdated:new Date().toISOString()};return D.set("usdt-pkr",o,k.PKR),o}}async function Ae(){let t=await fetch("https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":R},body:JSON.stringify({asset:"USDT",fiat:"PKR",merchantCheck:!1,page:1,payTypes:[],publisherType:null,rows:10,tradeType:"BUY"})});if(!t.ok)throw new Error(`Binance P2P API error: ${t.status}`);let o=(await t.json()).data?.slice(0,5)??[];if(o.length===0)throw new Error("No Binance P2P ads found for USDT/PKR");let a=o.map(r=>parseFloat(r.adv.price)).filter(r=>!isNaN(r));if(a.length===0)throw new Error("Invalid Binance P2P price data");let n=a.reduce((r,i)=>r+i,0)/a.length;return Number(n.toFixed(2))}async function Ee(){let e=(await P(["tether"],["pkr"])).tether?.pkr;if(e===void 0)throw new b("Could not fetch USDT/PKR rate from CoinGecko");return Number(e.toFixed(2))}async function M(t,e){let o=e.toLowerCase();if(o==="pkr")return{converted:t,rate:1,source:"direct"};if(o==="usdt"||o==="tether"){let u=await O();return{converted:Number((t*u.rate).toFixed(2)),rate:u.rate,source:u.source}}let a=f(o),r=(await P([a],["usd"]))[a]?.usd;if(r===void 0)throw new b(`Could not get USD price for ${e}`);let i=t*r,c=await O();return{converted:Number((i*c.rate).toFixed(2)),rate:Number((r*c.rate).toFixed(2)),source:`coingecko_usd + ${c.source}`}}async function se(t,e){let o=e.toLowerCase();if(o==="pkr")return{converted:t,rate:1,source:"direct"};let a=await O(),n=t/a.rate;if(o==="usdt"||o==="tether"||o==="usd")return{converted:Number(n.toFixed(6)),rate:Number((1/a.rate).toFixed(8)),source:a.source};let r=f(o),c=(await P([r],["usd"]))[r]?.usd;if(!c)throw new b(`Could not get USD price for ${e}`);let u=n/c;return{converted:Number(u.toFixed(8)),rate:Number((a.rate/c).toFixed(2)),source:`coingecko_usd + ${a.source}`}}var ie=B.object({coin:B.string().describe("Coin id or symbol: btc, bitcoin, eth, sol, etc"),vs_currency:B.string().default("usd").describe("Fiat: usd, eur, pkr, inr, gbp, etc")});async function ce(t){let{coin:e,vs_currency:o}=t,a=o.toLowerCase();if(a==="pkr"){let n=await O(),r=await Y(e,"usd"),i=(r.current_price??0)*n.rate;return{coin:r.id,symbol:r.symbol,name:r.name,price:s(i,2),vs_currency:"pkr","24h_change":r.price_change_percentage_24h?_(r.price_change_percentage_24h):null,market_cap:r.market_cap?s(r.market_cap*n.rate,0):null,market_cap_rank:r.market_cap_rank,total_volume:r.total_volume?s(r.total_volume*n.rate,0):null,high_24h:r.high_24h?s(r.high_24h*n.rate,2):null,low_24h:r.low_24h?s(r.low_24h*n.rate,2):null,ath:r.ath?s(r.ath*n.rate,2):null,ath_change_percent:r.ath_change_percentage?_(r.ath_change_percentage):null,usd_price:s(r.current_price,2),rate_source:n.source,last_updated:r.last_updated??new Date().toISOString()}}try{let n=await Y(e,a),r=n.current_price??0;return{coin:n.id,symbol:n.symbol,name:n.name,price:s(r,g(a,r)),vs_currency:a,"24h_change":n.price_change_percentage_24h?_(n.price_change_percentage_24h):null,market_cap:n.market_cap?s(n.market_cap,0):null,market_cap_rank:n.market_cap_rank,total_volume:n.total_volume?s(n.total_volume,0):null,high_24h:n.high_24h?s(n.high_24h,g(a,n.high_24h)):null,low_24h:n.low_24h?s(n.low_24h,g(a,n.low_24h)):null,ath:n.ath?s(n.ath,g(a,n.ath)):null,ath_change_percent:n.ath_change_percentage?_(n.ath_change_percentage):null,atl:n.atl?s(n.atl,g(a,n.atl)):null,last_updated:n.last_updated??new Date().toISOString()}}catch{let n=await S(e,a);return{coin:n.id,price:s(n.price,g(a,n.price)),vs_currency:a,"24h_change":n.change24h?_(n.change24h):null,last_updated:n.lastUpdated??new Date().toISOString()}}}import{z}from"zod";var ue=z.object({amount:z.number(),from:z.string(),to:z.string().describe("usd, pkr, btc, eth, etc")});async function pe(t){let{amount:e,from:o,to:a}=t,n=o.toLowerCase(),r=a.toLowerCase();if(n===r)return{amount:e,from:n,to:r,converted:e,rate:1,source:"direct"};if(r==="pkr"){let h=await M(e,n);return{amount:e,from:f(n),to:"pkr",converted:h.converted,rate:h.rate,source:h.source}}if(n==="pkr"){let h=await se(e,r);return{amount:e,from:"pkr",to:f(r),converted:h.converted,rate:h.rate,source:h.source}}let i=f(n),c=f(r),u=new Set(["usd","eur","gbp","jpy","inr","aud","cad","chf","cny"]);if(u.has(r)){let h=await S(n,r);return{amount:e,from:i,to:r,converted:s(e*h.price,2),rate:s(h.price,2),source:"coingecko"}}if(u.has(n)){let h=await S(r,n);return{amount:e,from:n,to:c,converted:s(e/h.price,8),rate:s(1/h.price,8),source:"coingecko"}}let p=await P([i,c],["usd"]),m=p[i]?.usd,l=p[c]?.usd;if(!m||!l)throw new Error(`Could not convert ${o} to ${a}`);let d=m/l,v=e*d;return{amount:e,from:i,to:c,converted:s(v,8),rate:s(d,8),source:"coingecko"}}import{z as le}from"zod";var Te=["eth","bnb","polygon","arbitrum","base","optimism"],me=le.object({chain:le.enum(Te).default("eth")});async function de(t){let e=t.chain,o=`gas:${e}`,a=H.get(o);if(a)return a;let n,r=ee[e],i=r?process.env[r.apiKeyEnv]:void 0;if(r&&i)try{n=await Ne(e,r.url,i)}catch{}n||(n=await Ge(e));let c=await $e(n);return H.set(o,c,k.GAS),c}function V(t){return t*K/1e9}async function $e(t){let e=Z[t.chain]??"ethereum",o=null;try{o=(await S(e,"usd")).price}catch{o=null}let a=V(t.low),n=V(t.average),r=V(t.high);return{...t,estimated_transfer:{gas_units:K,cost_native_low:s(a,8),cost_native_average:s(n,8),cost_native_high:s(r,8),cost_usd_low:o!==null?s(a*o,4):null,cost_usd_average:o!==null?s(n*o,4):null,cost_usd_high:o!==null?s(r*o,4):null,native_token:e}}}async function Ne(t,e,o){let a=`${e}?module=gastracker&action=gasoracle&apikey=${o}`,n=await fetch(a,{headers:{"User-Agent":R,Accept:"application/json"}});if(!n.ok)throw new Error(`Scan API error: ${n.status}`);let r=await n.json();if(r.status!=="1"||!r.result)throw new Error("Invalid scan gas oracle response");return{chain:t,low:s(parseFloat(r.result.SafeGasPrice??"0"),2),average:s(parseFloat(r.result.ProposeGasPrice??"0"),2),high:s(parseFloat(r.result.FastGasPrice??"0"),2),baseFee:r.result.suggestBaseFee?s(parseFloat(r.result.suggestBaseFee),2):null,unit:"gwei",last_updated:new Date().toISOString(),source:`${t}scan`}}async function Ge(t){let e=X[t];if(!e)throw new Error(`Unsupported chain: ${t}`);try{let o=await fetch(`https://api.blocknative.com/gasprices/blockprices?chainid=${e}`,{headers:{Accept:"application/json","User-Agent":R}});if(o.ok){let n=(await o.json()).blockPrices?.[0],r=n?.estimatedPrices??[];if(r.length>0){let i=[...r].sort((l,d)=>l.price-d.price),c=i[0]?.price??0,u=i[i.length-1]?.price??0,p=i[Math.floor(i.length/2)]?.price??c,m=n?.baseFeePerGas?n.baseFeePerGas/1e9:null;return{chain:t,low:s(c,2),average:s(p,2),high:s(u,2),baseFee:m!==null?s(m,2):null,unit:"gwei",last_updated:new Date().toISOString(),source:"blocknative"}}}}catch{}return qe(t)}function qe(t){let e={eth:{low:15,average:25,high:40},bnb:{low:3,average:5,high:8},polygon:{low:30,average:50,high:80},arbitrum:{low:.1,average:.2,high:.5},base:{low:.01,average:.05,high:.1},optimism:{low:.01,average:.05,high:.1}},o=e[t]??e.eth;return{chain:t,low:o.low,average:o.average,high:o.high,baseFee:null,unit:"gwei",last_updated:new Date().toISOString(),source:"static_estimate"}}import{z as A}from"zod";var he=A.object({buy_price:A.number(),sell_price:A.number(),quantity:A.number(),buy_fee_percent:A.number().default(0),sell_fee_percent:A.number().default(0),network_fee:A.number().default(0).describe("Fixed network/gas fee in the same unit as prices (e.g. USD)")});function ge(t){let e=t.buy_price,o=t.sell_price,a=t.quantity,n=t.buy_fee_percent??0,r=t.sell_fee_percent??0,i=t.network_fee??0,c=1+n/100,u=1-r/100,p=e*a*c,m=o*a*u,l=m-p,d=l-i,v=p+i,h=v>0?d/v*100:0,F=u>0?e*c/u+i/(a*u||1):e*c;return{buy_price:s(e,2),sell_price:s(o,2),quantity:s(a,8),buy_fee_percent:s(n,4),sell_fee_percent:s(r,4),network_fee:s(i,4),invested:s(p,2),returned:s(m,2),profit:s(d,2),profit_before_network_fee:s(l,2),roi_percent:_(h),break_even_price:s(F,2),is_profit:d>=0}}import{z as x}from"zod";var fe=x.object({coin:x.string(),date:x.string().optional().describe("Single date DD-MM-YYYY. Use with investment snapshot."),days:x.union([x.number(),x.string()]).optional().describe("Chart range: 1, 7, 14, 30, 90, 180, 365, or max"),vs_currency:x.string().default("usd"),investment:x.number().default(1e3).describe("Hypothetical investment amount on the historical date")});function je(t){let e=t.match(/^(\d{2})-(\d{2})-(\d{4})$/);if(!e)throw new Error(`Invalid date format: ${t}. Expected DD-MM-YYYY (e.g. 15-01-2024)`);let[,o,a,n]=e,r=`${o}-${a}-${n}`,i=`${n}-${a}-${o}`,c=new Date(i);if(isNaN(c.getTime()))throw new Error(`Invalid date: ${t}`);let u=new Date;if(u.setHours(23,59,59,999),c>u)throw new Error(`Date cannot be in the future: ${t}`);return{formatted:r,iso:i}}function Oe(t){if(t.length===0)return null;let e=t.map(m=>m.price),o=e[0],a=e[e.length-1],n=Math.max(...e),r=Math.min(...e),i=a-o,c=o>0?i/o*100:0,u=Math.max(1,Math.floor(t.length/12)),p=t.filter((m,l)=>l%u===0||l===t.length-1).map(m=>({time:new Date(m.timestamp).toISOString(),price:s(m.price,g("usd",m.price))}));return{start_price:s(o,g("usd",o)),end_price:s(a,g("usd",a)),high:s(n,g("usd",n)),low:s(r,g("usd",r)),change:s(i,4),change_percent:_(c),points:t.length,sample:p}}async function _e(t){let e=(t.vs_currency??"usd").toLowerCase(),o=t.investment??1e3,a=f(t.coin);if(!t.date&&!t.days)throw new Error("Provide either date (DD-MM-YYYY) for a point-in-time price, or days (e.g. 7, 30, 90) for a range chart.");if(t.days!==void 0&&t.days!==null){let l=await oe(t.coin,e,t.days),d=Oe(l.prices);return{mode:"range",coin:a,vs_currency:e,days:String(t.days),summary:d}}let{formatted:n}=je(t.date),r=await ne(t.coin,n,e),i=await ae(t.coin,e),c=r.price>0?o/r.price:0,u=c*i,p=u-o,m=o>0?p/o*100:0;return{mode:"point",coin:a,date:n,vs_currency:e,historical_price:s(r.price,g(e,r.price)),current_price:s(i,g(e,i)),price_change:s(i-r.price,4),price_change_percent:_(r.price>0?(i-r.price)/r.price*100:0),investment_then:{invested:o,coins_bought:s(c,8),worth_now:s(u,2),gain_loss:s(p,2),gain_loss_percent:_(m)}}}import{z as W}from"zod";var be=W.object({query:W.string().describe("Name or symbol to search, e.g. pepe, solana, wif"),limit:W.number().int().min(1).max(50).default(10)});async function ye(t){let e=t.limit??10,o=await re(t.query,e);return{query:t.query,count:o.length,results:o}}import{z as L}from"zod";var we=L.object({vs_currency:L.string().default("usd"),limit:L.number().int().min(1).max(50).default(10),order:L.enum(["market_cap_desc","market_cap_asc","volume_desc","volume_asc","id_asc","id_desc"]).default("market_cap_desc")});async function ke(t){let e=(t.vs_currency??"usd").toLowerCase(),o=t.limit??10,a=t.order??"market_cap_desc",r=(await j(e,{perPage:o,order:a})).map(p=>({id:p.id,symbol:p.symbol,name:p.name,rank:p.market_cap_rank,price:s(p.current_price??0,g(e,p.current_price??0)),market_cap:p.market_cap?s(p.market_cap,0):null,total_volume:p.total_volume?s(p.total_volume,0):null,"24h_change":p.price_change_percentage_24h?_(p.price_change_percentage_24h):null})),i=r.filter(p=>p["24h_change"]!==null),c=[...i].sort((p,m)=>(m["24h_change"]??0)-(p["24h_change"]??0)).slice(0,3),u=[...i].sort((p,m)=>(p["24h_change"]??0)-(m["24h_change"]??0)).slice(0,3);return{vs_currency:e,order:a,count:r.length,coins:r,top_gainers:c,top_losers:u}}import{z as T}from"zod";var ve=T.object({coins:T.union([T.array(T.string()).min(2).max(5),T.string().describe("Comma-separated symbols, e.g. btc,eth,sol")]).describe("2\u20135 coins to compare"),vs_currency:T.string().default("usd")});function Fe(t){let e=Array.isArray(t)?t:t.split(/[,\s]+/).map(a=>a.trim()).filter(Boolean),o=[...new Set(e.map(a=>a.toLowerCase()))];if(o.length<2||o.length>5)throw new Error("Provide between 2 and 5 coins to compare");return o}async function Pe(t){let e=(t.vs_currency??"usd").toLowerCase(),o=Fe(t.coins),a=o.map(f),n=await j(e,{ids:a,perPage:a.length}),r=new Map(n.map(m=>[m.id,m])),i=a.map((m,l)=>{let d=r.get(m);return d?{input:o[l],id:d.id,found:!0,symbol:d.symbol,name:d.name,rank:d.market_cap_rank,price:s(d.current_price??0,g(e,d.current_price??0)),market_cap:d.market_cap?s(d.market_cap,0):null,total_volume:d.total_volume?s(d.total_volume,0):null,"24h_change":d.price_change_percentage_24h?_(d.price_change_percentage_24h):null}:{input:o[l],id:m,found:!1}}),c=i.filter(m=>m.found),u=[...c].sort((m,l)=>(l["24h_change"]??-1/0)-(m["24h_change"]??-1/0))[0],p=[...c].sort((m,l)=>(l.market_cap??0)-(m.market_cap??0))[0];return{vs_currency:e,coins:i,highlights:{best_24h_performer:u?.id??null,largest_market_cap:p?.id??null}}}import{z as $}from"zod";var De=$.object({coin:$.string(),amount:$.number().positive()}),Ce=$.object({holdings:$.array(De).min(1).max(50).describe("List of { coin, amount } holdings"),vs_currency:$.string().default("usd")});async function Se(t){let e=(t.vs_currency??"usd").toLowerCase(),o=t.holdings,a=o.map(l=>f(l.coin)),n=[...new Set(a)],r=e==="pkr"?"usd":e,i=await P(n,[r]),c=null,u=null;if(e==="pkr"){let l=await M(1,"usdt");c=l.rate,u=l.source}let p=o.map((l,d)=>{let v=a[d],h=i[v]?.[r];if(h===void 0)return{coin:v,amount:l.amount,found:!1,value:null};let F=l.amount*h,U=h;return e==="pkr"&&c!==null&&(F*=c,U*=c),{coin:v,amount:l.amount,found:!0,unit_price:s(U,g(e,U)),value:s(F,2)}}),m=p.reduce((l,d)=>l+(d.found&&d.value!==null?d.value:0),0);return{vs_currency:e,holdings:p,total_value:s(m,2),missing:p.filter(l=>!l.found).map(l=>l.coin),...u?{rate_source:u}:{}}}var Ke=[{name:"get_price",description:"Get live price of any cryptocurrency in any fiat, with market cap, volume, rank, ATH/ATL. Uses CoinGecko.",inputSchema:{type:"object",properties:{coin:{type:"string",description:"Coin id or symbol: btc, bitcoin, eth, sol, etc"},vs_currency:{type:"string",description:"Fiat: usd, eur, pkr, inr, gbp, etc",default:"usd"}},required:["coin"]}},{name:"convert",description:"Convert crypto amount to fiat/crypto. Optimized PKR via Binance P2P when requested.",inputSchema:{type:"object",properties:{amount:{type:"number"},from:{type:"string"},to:{type:"string",description:"usd, eur, pkr, btc, eth, etc"}},required:["amount","from","to"]}},{name:"gas_tracker",description:"Live gas fees for EVM chains (eth, bnb, polygon, arbitrum, base, optimism) with estimated transfer cost in native + USD.",inputSchema:{type:"object",properties:{chain:{type:"string",enum:["eth","bnb","polygon","arbitrum","base","optimism"],default:"eth"}}}},{name:"profit_calc",description:"Calculate crypto profit/loss, ROI, break-even. Supports exchange fees and fixed network/gas fee. No API needed.",inputSchema:{type:"object",properties:{buy_price:{type:"number"},sell_price:{type:"number"},quantity:{type:"number"},buy_fee_percent:{type:"number",default:0},sell_fee_percent:{type:"number",default:0},network_fee:{type:"number",default:0,description:"Fixed network fee in same unit as prices"}},required:["buy_price","sell_price","quantity"]}},{name:"historical_price",description:"Historical price on a date (DD-MM-YYYY) with investment snapshot, or a range chart via days (1/7/30/90/365/max).",inputSchema:{type:"object",properties:{coin:{type:"string"},date:{type:"string",description:"DD-MM-YYYY for point-in-time"},days:{description:"Range chart: 1, 7, 14, 30, 90, 180, 365, or max"},vs_currency:{type:"string",default:"usd"},investment:{type:"number",default:1e3,description:"Hypothetical investment on the historical date"}},required:["coin"]}},{name:"search_coin",description:"Search cryptocurrencies by name or symbol. Returns CoinGecko ids for use in other tools.",inputSchema:{type:"object",properties:{query:{type:"string"},limit:{type:"number",default:10}},required:["query"]}},{name:"top_coins",description:"Top cryptocurrencies by market cap or volume, with gainers/losers among the result set.",inputSchema:{type:"object",properties:{vs_currency:{type:"string",default:"usd"},limit:{type:"number",default:10},order:{type:"string",enum:["market_cap_desc","market_cap_asc","volume_desc","volume_asc"],default:"market_cap_desc"}}}},{name:"compare_coins",description:"Compare 2\u20135 cryptocurrencies side by side (price, mcap, volume, 24h change).",inputSchema:{type:"object",properties:{coins:{description:"Array or comma-separated list, e.g. btc,eth,sol"},vs_currency:{type:"string",default:"usd"}},required:["coins"]}},{name:"portfolio_value",description:"Value a portfolio of holdings in any fiat (batch pricing). Pass [{coin, amount}, ...].",inputSchema:{type:"object",properties:{holdings:{type:"array",items:{type:"object",properties:{coin:{type:"string"},amount:{type:"number"}},required:["coin","amount"]}},vs_currency:{type:"string",default:"usd"}},required:["holdings"]}}],He=[{name:"market-overview",description:"Global crypto market snapshot using top coins and gainers/losers",arguments:[{name:"vs_currency",description:"Fiat currency (default usd)",required:!1},{name:"limit",description:"How many top coins (default 10)",required:!1}]},{name:"analyze-coin",description:"Analyze a coin: live price, 7d history, and optional conversion",arguments:[{name:"coin",description:"Coin symbol or id",required:!0},{name:"vs_currency",description:"Fiat currency (default usd)",required:!1}]},{name:"trade-pnl",description:"Walk through a profit/loss calculation for a completed trade",arguments:[{name:"buy_price",description:"Entry price",required:!0},{name:"sell_price",description:"Exit price",required:!0},{name:"quantity",description:"Amount traded",required:!0},{name:"fees",description:"Optional fee notes (percent or network)",required:!1}]},{name:"gas-check",description:"Check current gas and estimated transfer cost for an EVM chain",arguments:[{name:"chain",description:"eth, bnb, polygon, arbitrum, base, or optimism",required:!1}]}];function J(t){return{content:[{type:"text",text:t}],isError:!0}}function C(t){return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}}function xe(t){t.setRequestHandler(ze,async()=>({tools:Ke})),t.setRequestHandler(Le,async()=>({prompts:He})),t.setRequestHandler(Ue,async e=>{let{name:o,arguments:a}=e.params,n=a??{};switch(o){case"market-overview":{let r=n.vs_currency??"usd",i=n.limit??"10";return{description:"Market overview workflow",messages:[{role:"user",content:{type:"text",text:`Give me a crypto market overview in ${r}. Use the top_coins tool with limit ${i}. Summarize leaders, notable gainers/losers, and overall tone.`}}]}}case"analyze-coin":{let r=n.coin;if(!r)throw new Error("coin is required");let i=n.vs_currency??"usd";return{description:`Analyze ${r}`,messages:[{role:"user",content:{type:"text",text:`Analyze ${r} in ${i}. 1) Call get_price. 2) Call historical_price with days=7. 3) Summarize price, market cap, 24h move, and 7-day trend.`}}]}}case"trade-pnl":{let{buy_price:r,sell_price:i,quantity:c,fees:u}=n;if(!r||!i||!c)throw new Error("buy_price, sell_price, and quantity are required");return{description:"Trade P&L workflow",messages:[{role:"user",content:{type:"text",text:`Calculate P&L with profit_calc: buy_price=${r}, sell_price=${i}, quantity=${c}.${u?` Fee notes: ${u}.`:""} Explain profit, ROI, and break-even clearly.`}}]}}case"gas-check":{let r=n.chain??"eth";return{description:`Gas check for ${r}`,messages:[{role:"user",content:{type:"text",text:`Check gas on ${r} with gas_tracker. Report low/average/high gwei and estimated simple-transfer cost in USD. Advise whether fees look cheap or expensive.`}}]}}default:throw new Error(`Unknown prompt: ${o}`)}}),t.setRequestHandler(Me,async e=>{let{name:o,arguments:a}=e.params;try{switch(o){case"get_price":return C(await ce(ie.parse(a??{})));case"convert":return C(await pe(ue.parse(a??{})));case"gas_tracker":return C(await de(me.parse(a??{})));case"profit_calc":return C(ge(he.parse(a??{})));case"historical_price":return C(await _e(fe.parse(a??{})));case"search_coin":return C(await ye(be.parse(a??{})));case"top_coins":return C(await ke(we.parse(a??{})));case"compare_coins":return C(await Pe(ve.parse(a??{})));case"portfolio_value":return C(await Se(Ce.parse(a??{})));default:return J(`Unknown tool: ${o}`)}}catch(n){if(n instanceof b&&n.retryable)return J(`Rate limit hit: ${n.message}. Wait a few seconds and retry.`);let r=n instanceof Error?n.message:"Unknown error occurred";return J(r)}})}var Re=Number(process.env.PORT??3e3);function We(){let t=new Be({name:"mcp-crypto-toolkit",version:"1.1.0",description:"Live crypto prices, conversion, gas tracker, portfolio tools and calculators for AI agents"},{capabilities:{tools:{},prompts:{}}});return xe(t),t}var Je=Ye(async(t,e)=>{try{if(t.method==="GET"&&(t.url==="/"||t.url==="/health")){e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!0,name:"mcp-crypto-toolkit",mcp:"/mcp"}));return}if(t.url?.startsWith("/mcp")){let o=We(),a=new Ve({sessionIdGenerator:void 0,enableJsonResponse:!0});e.on("close",()=>{a.close(),o.close()}),await o.connect(a),await a.handleRequest(t,e);return}e.writeHead(404,{"Content-Type":"text/plain"}),e.end("Not found. Use POST /mcp or GET /health")}catch(o){console.error("HTTP error:",o),e.headersSent||(e.writeHead(500,{"Content-Type":"text/plain"}),e.end("Internal server error"))}});Je.listen(Re,()=>{console.error(`mcp-crypto-toolkit HTTP listening on http://localhost:${Re}/mcp`)});
|
|
2
|
+
import{createServer as st}from"http";import{StreamableHTTPServerTransport as Me}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{Server as at}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as Xe,ListToolsRequestSchema as Ze,ListPromptsRequestSchema as et,GetPromptRequestSchema as tt}from"@modelcontextprotocol/sdk/types.js";var te={btc:"bitcoin",bitcoin:"bitcoin",eth:"ethereum",ethereum:"ethereum",usdt:"tether",tether:"tether",bnb:"binancecoin",binancecoin:"binancecoin",sol:"solana",solana:"solana",xrp:"ripple",ripple:"ripple",usdc:"usd-coin","usd-coin":"usd-coin",ada:"cardano",cardano:"cardano",avax:"avalanche-2",avalanche:"avalanche-2",doge:"dogecoin",dogecoin:"dogecoin",dot:"polkadot",polkadot:"polkadot",trx:"tron",tron:"tron",link:"chainlink",chainlink:"chainlink",matic:"matic-network",polygon:"matic-network","matic-network":"matic-network",shib:"shiba-inu","shiba-inu":"shiba-inu",ltc:"litecoin",litecoin:"litecoin",bch:"bitcoin-cash","bitcoin-cash":"bitcoin-cash",uni:"uniswap",uniswap:"uniswap",atom:"cosmos",cosmos:"cosmos",xlm:"stellar",stellar:"stellar",xmr:"monero",monero:"monero",etc:"ethereum-classic","ethereum-classic":"ethereum-classic",fil:"filecoin",filecoin:"filecoin",apt:"aptos",aptos:"aptos",arb:"arbitrum",arbitrum:"arbitrum",op:"optimism",optimism:"optimism",near:"near",vet:"vechain",vechain:"vechain",icp:"internet-computer",hbar:"hedera-hashgraph",algo:"algorand",algorand:"algorand",qnt:"quant-network",eos:"eos",aave:"aave",grt:"the-graph",sand:"the-sandbox",mana:"decentraland",axs:"axie-infinity",theta:"theta-token",ftm:"fantom",fantom:"fantom",xtz:"tezos",tezos:"tezos",rune:"thorchain",snx:"havven",crv:"curve-dao-token",mkr:"maker",maker:"maker",comp:"compound-governance-token",ldo:"lido-dao",sui:"sui",sei:"sei-network",inj:"injective-protocol",tia:"celestia",pepe:"pepe",wif:"dogwifcoin",bonk:"bonk",floki:"floki",render:"render-token",rndr:"render-token",fet:"fetch-ai",imx:"immutable-x",gala:"gala",enj:"enjincoin",bat:"basic-attention-token",zec:"zcash",dash:"dash",neo:"neo",kcs:"kucoin-shares",okb:"okb",ton:"the-open-network",kas:"kaspa",stx:"blockstack",rpl:"rocket-pool",blur:"blur",pendle:"pendle",jup:"jupiter-exchange-solana",pyth:"pyth-network",wld:"worldcoin-wld",strk:"starknet",ethfi:"ether-fi",ena:"ethena",ondo:"ondo-finance",not:"notcoin",bome:"book-of-meme",ar:"arweave",flow:"flow",mina:"mina-protocol",ro:"ronin",ronin:"ronin",egld:"elrond-erd-2",egld_m:"elrond-erd-2",cake:"pancakeswap-token","1inch":"1inch",ens:"ethereum-name-service",chz:"chiliz",hot:"holotoken",zil:"zilliqa",icx:"icon",waves:"waves",kava:"kava",celo:"celo",rose:"oasis-network",one:"harmony",iota:"iota",xem:"nem",sc:"siacoin",dcr:"decred",rvn:"ravencoin",zen:"zencash",ont:"ontology",qtum:"qtum",btt:"bittorrent",hnt:"helium",lrc:"loopring",ankr:"ankr",omg:"omisego",storj:"storj",skl:"skale",celr:"celer-network",coti:"coti",ren:"republic-protocol",band:"band-protocol",ocean:"ocean-protocol",api3:"api3",nmr:"numeraire",bal:"balancer",yfi:"yearn-finance",sushi:"sushi",dydx:"dydx-chain",gmx:"gmx",magic:"magic",rdnt:"radiant-capital",joe:"joe",woo:"woo-network",perp:"perpetual-protocol",lqty:"liquity",cvx:"convex-finance",frax:"frax",fxn:"fxn",usdd:"usdd",tusd:"true-usd",dai:"dai",frax_usd:"frax"};var re={eth:1,bnb:56,polygon:137,arbitrum:42161,base:8453,optimism:10},ne={eth:"ethereum",bnb:"binancecoin",polygon:"matic-network",arbitrum:"ethereum",base:"ethereum",optimism:"ethereum"},B=21e3,oe={eth:{url:"https://api.etherscan.io/api",apiKeyEnv:"ETHERSCAN_API_KEY"},bnb:{url:"https://api.bscscan.com/api",apiKeyEnv:"BSCSCAN_API_KEY"},polygon:{url:"https://api.polygonscan.com/api",apiKeyEnv:"POLYGONSCAN_API_KEY"},arbitrum:{url:"https://api.arbiscan.io/api",apiKeyEnv:"ARBISCAN_API_KEY"},base:{url:"https://api.basescan.org/api",apiKeyEnv:"BASESCAN_API_KEY"},optimism:{url:"https://api-optimistic.etherscan.io/api",apiKeyEnv:"OPTIMISM_API_KEY"}},I="mcp-crypto-toolkit/1.0.0 (https://github.com/nad33mahm3d/mcp-crypto-toolkit)";var T=class{store=new Map;timers=new Map;get(e){let o=this.store.get(e);if(o){if(Date.now()>o.expiresAt){this.delete(e);return}return o.value}}set(e,o,a){let n=this.timers.get(e);n&&clearTimeout(n),this.store.set(e,{value:o,expiresAt:Date.now()+a});let r=setTimeout(()=>{this.delete(e)},a);r.unref?.(),this.timers.set(e,r)}clear(){for(let e of this.timers.values())clearTimeout(e);this.timers.clear(),this.store.clear()}delete(e){let o=this.timers.get(e);o&&(clearTimeout(o),this.timers.delete(e)),this.store.delete(e)}},v=new T,V=new T,O=new T,L=new T,k={PRICE:6e4,GAS:15e3,HISTORICAL:36e5,PKR:3e5,SEARCH:12e4,TRENDING:18e4};var N="https://api.coingecko.com/api/v3",w=class extends Error{constructor(o,a,n=!1){super(o);this.status=a;this.retryable=n;this.name="CoinGeckoError"}status;retryable};function f(t){let e=t.toLowerCase().trim();return te[e]??e}function Le(){let t=process.env.COINGECKO_API_KEY?.trim();return t?t.startsWith("CG-")&&process.env.COINGECKO_PRO==="1"?{"x-cg-pro-api-key":t}:{"x-cg-demo-api-key":t}:{}}async function E(t,e={},o=3){let a;for(let n=0;n<o;n++)try{let r=await fetch(t,{...e,headers:{Accept:"application/json","User-Agent":I,...Le(),...e.headers??{}}});if(r.status===429){let i=Math.min(1e3*2**n,1e4);if(n<o-1){await ae(i);continue}throw new w("CoinGecko rate limit exceeded (429). Wait a few seconds, or set COINGECKO_API_KEY for higher limits.",429,!0)}if(!r.ok)throw new w(`CoinGecko API error: ${r.status} ${r.statusText}`,r.status,r.status>=500);return r}catch(r){if(r instanceof w)throw r;a=r instanceof Error?r:new Error(String(r)),n<o-1&&await ae(1e3*2**n)}throw a??new Error("Fetch failed after retries")}async function P(t,e){let o=[...new Set(t.map(f))],a=[...new Set(e.map(p=>p.toLowerCase()))],n=`simple:${o.sort().join(",")}:${a.sort().join(",")}`,r=v.get(n);if(r)return r;let i=new URLSearchParams({ids:o.join(","),vs_currencies:a.join(","),include_24hr_change:"true",include_last_updated_at:"true"}),u=await(await E(`${N}/simple/price?${i}`)).json();return v.set(n,u,k.PRICE),u}async function x(t,e){let o=f(t),a=e.toLowerCase(),r=(await P([o],[a]))[o];if(!r)throw new w(`Coin not found: ${t} (id: ${o})`);let i=r[a];if(i===void 0)throw new w(`Currency not supported: ${e}`);let c=`${a}_24h_change`,u="last_updated_at";return{id:o,price:Number(i),change24h:r[c]!==void 0?Number(r[c]):void 0,lastUpdated:r[u]!==void 0?new Date(Number(r[u])*1e3).toISOString():new Date().toISOString()}}async function F(t,e={}){let o=t.toLowerCase(),a=e.perPage??10,n=e.page??1,r=e.order??"market_cap_desc",i=e.ids?.map(f),c=`markets:${o}:${i?.sort().join(",")??"all"}:${a}:${n}:${r}`,u=v.get(c);if(u)return u;let p=new URLSearchParams({vs_currency:o,order:r,per_page:String(a),page:String(n),sparkline:"false",price_change_percentage:"24h"});i?.length&&p.set("ids",i.join(","));let l=await(await E(`${N}/coins/markets?${p}`)).json();return v.set(c,l,k.PRICE),l}async function J(t,e){let o=f(t),n=(await F(e,{ids:[o],perPage:1}))[0];if(!n)throw new w(`Coin not found: ${t} (id: ${o})`);return n}async function se(t,e=10){let o=t.trim();if(!o)return[];let a=`search:${o.toLowerCase()}:${e}`,n=v.get(a);if(n)return n;let c=((await(await E(`${N}/search?query=${encodeURIComponent(o)}`)).json()).coins??[]).slice(0,e).map(u=>({id:u.id,name:u.name,symbol:u.symbol,market_cap_rank:u.market_cap_rank}));return v.set(a,c,k.PRICE),c}async function ie(t,e,o){let a=f(t),n=o.toLowerCase(),r=`hist:${a}:${e}:${n}`,i=O.get(r);if(i)return i;let u=await(await E(`${N}/coins/${a}/history?date=${encodeURIComponent(e)}&localization=false`)).json();if(u.error||!u.market_data?.current_price?.[n])throw new w(u.error??`Historical price not found for ${t} on ${e}`);let p={id:a,date:e,price:u.market_data.current_price[n],vsCurrency:n};return O.set(r,p,k.HISTORICAL),p}async function ce(t,e,o){let a=f(t),n=e.toLowerCase(),r=String(o),i=`chart:${a}:${n}:${r}`,c=O.get(i);if(c)return c;let u=new URLSearchParams({vs_currency:n,days:r}),l=((await(await E(`${N}/coins/${a}/market_chart?${u}`)).json()).prices??[]).map(([C,g])=>({timestamp:C,price:g})),d={id:a,vs:n,days:r,prices:l};return O.set(i,d,k.HISTORICAL),d}async function ue(t,e){return(await x(t,e)).price}async function pe(){let t="trending:coins",e=v.get(t);if(e)return e;let n=((await(await E(`${N}/search/trending`)).json()).coins??[]).map(({item:r})=>({id:r.id,name:r.name,symbol:r.symbol,market_cap_rank:r.market_cap_rank,score:r.score,price_btc:r.price_btc??null,price_usd:typeof r.data?.price=="number"?s(r.data.price,h("usd",r.data.price)):null,change_24h_usd:typeof r.data?.price_change_percentage_24h?.usd=="number"?y(r.data.price_change_percentage_24h.usd):null}));return v.set(t,n,k.TRENDING),n}async function le(){let t="fear-greed:1",e=v.get(t);if(e)return e;let n=(await(await E("https://api.alternative.me/fng/?limit=1")).json()).data?.[0];if(!n)throw new w("Fear & Greed index unavailable");let r={value:Number(n.value),classification:n.value_classification,updatedAt:new Date(Number(n.timestamp)*1e3).toISOString()};return v.set(t,r,k.TRENDING),r}function ae(t){return new Promise(e=>setTimeout(e,t))}function s(t,e=2){return Number.isFinite(t)?Number(t.toFixed(e)):0}function y(t,e=2){return Number.isFinite(t)?Number(t.toFixed(e)):0}function h(t,e){return t==="btc"||t==="eth"?8:e>=1?2:e>=.01?4:8}import{z as W}from"zod";async function M(){let t=L.get("usdt-pkr");if(t)return t;try{let o={rate:await qe(),source:"binance_p2p",lastUpdated:new Date().toISOString()};return L.set("usdt-pkr",o,k.PKR),o}catch{let o={rate:await ze(),source:"coingecko",lastUpdated:new Date().toISOString()};return L.set("usdt-pkr",o,k.PKR),o}}async function qe(){let t=await fetch("https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":I},body:JSON.stringify({asset:"USDT",fiat:"PKR",merchantCheck:!1,page:1,payTypes:[],publisherType:null,rows:10,tradeType:"BUY"})});if(!t.ok)throw new Error(`Binance P2P API error: ${t.status}`);let o=(await t.json()).data?.slice(0,5)??[];if(o.length===0)throw new Error("No Binance P2P ads found for USDT/PKR");let a=o.map(r=>parseFloat(r.adv.price)).filter(r=>!isNaN(r));if(a.length===0)throw new Error("Invalid Binance P2P price data");let n=a.reduce((r,i)=>r+i,0)/a.length;return Number(n.toFixed(2))}async function ze(){let e=(await P(["tether"],["pkr"])).tether?.pkr;if(e===void 0)throw new w("Could not fetch USDT/PKR rate from CoinGecko");return Number(e.toFixed(2))}async function q(t,e){let o=e.toLowerCase();if(o==="pkr")return{converted:t,rate:1,source:"direct"};if(o==="usdt"||o==="tether"){let u=await M();return{converted:Number((t*u.rate).toFixed(2)),rate:u.rate,source:u.source}}let a=f(o),r=(await P([a],["usd"]))[a]?.usd;if(r===void 0)throw new w(`Could not get USD price for ${e}`);let i=t*r,c=await M();return{converted:Number((i*c.rate).toFixed(2)),rate:Number((r*c.rate).toFixed(2)),source:`coingecko_usd + ${c.source}`}}async function me(t,e){let o=e.toLowerCase();if(o==="pkr")return{converted:t,rate:1,source:"direct"};let a=await M(),n=t/a.rate;if(o==="usdt"||o==="tether"||o==="usd")return{converted:Number(n.toFixed(6)),rate:Number((1/a.rate).toFixed(8)),source:a.source};let r=f(o),c=(await P([r],["usd"]))[r]?.usd;if(!c)throw new w(`Could not get USD price for ${e}`);let u=n/c;return{converted:Number(u.toFixed(8)),rate:Number((a.rate/c).toFixed(2)),source:`coingecko_usd + ${a.source}`}}var de=W.object({coin:W.string().describe("Coin id or symbol: btc, bitcoin, eth, sol, etc"),vs_currency:W.string().default("usd").describe("Fiat: usd, eur, pkr, inr, gbp, etc")});async function he(t){let{coin:e,vs_currency:o}=t,a=o.toLowerCase();if(a==="pkr"){let n=await M(),r=await J(e,"usd"),i=(r.current_price??0)*n.rate;return{coin:r.id,symbol:r.symbol,name:r.name,price:s(i,2),vs_currency:"pkr","24h_change":r.price_change_percentage_24h?y(r.price_change_percentage_24h):null,market_cap:r.market_cap?s(r.market_cap*n.rate,0):null,market_cap_rank:r.market_cap_rank,total_volume:r.total_volume?s(r.total_volume*n.rate,0):null,high_24h:r.high_24h?s(r.high_24h*n.rate,2):null,low_24h:r.low_24h?s(r.low_24h*n.rate,2):null,ath:r.ath?s(r.ath*n.rate,2):null,ath_change_percent:r.ath_change_percentage?y(r.ath_change_percentage):null,usd_price:s(r.current_price,2),rate_source:n.source,last_updated:r.last_updated??new Date().toISOString()}}try{let n=await J(e,a),r=n.current_price??0;return{coin:n.id,symbol:n.symbol,name:n.name,price:s(r,h(a,r)),vs_currency:a,"24h_change":n.price_change_percentage_24h?y(n.price_change_percentage_24h):null,market_cap:n.market_cap?s(n.market_cap,0):null,market_cap_rank:n.market_cap_rank,total_volume:n.total_volume?s(n.total_volume,0):null,high_24h:n.high_24h?s(n.high_24h,h(a,n.high_24h)):null,low_24h:n.low_24h?s(n.low_24h,h(a,n.low_24h)):null,ath:n.ath?s(n.ath,h(a,n.ath)):null,ath_change_percent:n.ath_change_percentage?y(n.ath_change_percentage):null,atl:n.atl?s(n.atl,h(a,n.atl)):null,last_updated:n.last_updated??new Date().toISOString()}}catch{let n=await x(e,a);return{coin:n.id,price:s(n.price,h(a,n.price)),vs_currency:a,"24h_change":n.change24h?y(n.change24h):null,last_updated:n.lastUpdated??new Date().toISOString()}}}import{z}from"zod";var ge=z.object({amount:z.number(),from:z.string(),to:z.string().describe("usd, pkr, btc, eth, etc")});async function fe(t){let{amount:e,from:o,to:a}=t,n=o.toLowerCase(),r=a.toLowerCase();if(n===r)return{amount:e,from:n,to:r,converted:e,rate:1,source:"direct"};if(r==="pkr"){let g=await q(e,n);return{amount:e,from:f(n),to:"pkr",converted:g.converted,rate:g.rate,source:g.source}}if(n==="pkr"){let g=await me(e,r);return{amount:e,from:"pkr",to:f(r),converted:g.converted,rate:g.rate,source:g.source}}let i=f(n),c=f(r),u=new Set(["usd","eur","gbp","jpy","inr","aud","cad","chf","cny"]);if(u.has(r)){let g=await x(n,r);return{amount:e,from:i,to:r,converted:s(e*g.price,2),rate:s(g.price,2),source:"coingecko"}}if(u.has(n)){let g=await x(r,n);return{amount:e,from:n,to:c,converted:s(e/g.price,8),rate:s(1/g.price,8),source:"coingecko"}}let p=await P([i,c],["usd"]),m=p[i]?.usd,l=p[c]?.usd;if(!m||!l)throw new Error(`Could not convert ${o} to ${a}`);let d=m/l,C=e*d;return{amount:e,from:i,to:c,converted:s(C,8),rate:s(d,8),source:"coingecko"}}import{z as _e}from"zod";var Ue=["eth","bnb","polygon","arbitrum","base","optimism"],be=_e.object({chain:_e.enum(Ue).default("eth")});async function ye(t){let e=t.chain,o=`gas:${e}`,a=V.get(o);if(a)return a;let n,r=oe[e],i=r?process.env[r.apiKeyEnv]:void 0;if(r&&i)try{n=await Ke(e,r.url,i)}catch{}n||(n=await Ye(e));let c=await He(n);return V.set(o,c,k.GAS),c}function Q(t){return t*B/1e9}async function He(t){let e=ne[t.chain]??"ethereum",o=null;try{o=(await x(e,"usd")).price}catch{o=null}let a=Q(t.low),n=Q(t.average),r=Q(t.high);return{...t,estimated_transfer:{gas_units:B,cost_native_low:s(a,8),cost_native_average:s(n,8),cost_native_high:s(r,8),cost_usd_low:o!==null?s(a*o,4):null,cost_usd_average:o!==null?s(n*o,4):null,cost_usd_high:o!==null?s(r*o,4):null,native_token:e}}}async function Ke(t,e,o){let a=`${e}?module=gastracker&action=gasoracle&apikey=${o}`,n=await fetch(a,{headers:{"User-Agent":I,Accept:"application/json"}});if(!n.ok)throw new Error(`Scan API error: ${n.status}`);let r=await n.json();if(r.status!=="1"||!r.result)throw new Error("Invalid scan gas oracle response");return{chain:t,low:s(parseFloat(r.result.SafeGasPrice??"0"),2),average:s(parseFloat(r.result.ProposeGasPrice??"0"),2),high:s(parseFloat(r.result.FastGasPrice??"0"),2),baseFee:r.result.suggestBaseFee?s(parseFloat(r.result.suggestBaseFee),2):null,unit:"gwei",last_updated:new Date().toISOString(),source:`${t}scan`}}async function Ye(t){let e=re[t];if(!e)throw new Error(`Unsupported chain: ${t}`);try{let o=await fetch(`https://api.blocknative.com/gasprices/blockprices?chainid=${e}`,{headers:{Accept:"application/json","User-Agent":I}});if(o.ok){let n=(await o.json()).blockPrices?.[0],r=n?.estimatedPrices??[];if(r.length>0){let i=[...r].sort((l,d)=>l.price-d.price),c=i[0]?.price??0,u=i[i.length-1]?.price??0,p=i[Math.floor(i.length/2)]?.price??c,m=n?.baseFeePerGas?n.baseFeePerGas/1e9:null;return{chain:t,low:s(c,2),average:s(p,2),high:s(u,2),baseFee:m!==null?s(m,2):null,unit:"gwei",last_updated:new Date().toISOString(),source:"blocknative"}}}}catch{}return Be(t)}function Be(t){let e={eth:{low:15,average:25,high:40},bnb:{low:3,average:5,high:8},polygon:{low:30,average:50,high:80},arbitrum:{low:.1,average:.2,high:.5},base:{low:.01,average:.05,high:.1},optimism:{low:.01,average:.05,high:.1}},o=e[t]??e.eth;return{chain:t,low:o.low,average:o.average,high:o.high,baseFee:null,unit:"gwei",last_updated:new Date().toISOString(),source:"static_estimate"}}import{z as A}from"zod";var we=A.object({buy_price:A.number(),sell_price:A.number(),quantity:A.number(),buy_fee_percent:A.number().default(0),sell_fee_percent:A.number().default(0),network_fee:A.number().default(0).describe("Fixed network/gas fee in the same unit as prices (e.g. USD)")});function ke(t){let e=t.buy_price,o=t.sell_price,a=t.quantity,n=t.buy_fee_percent??0,r=t.sell_fee_percent??0,i=t.network_fee??0,c=1+n/100,u=1-r/100,p=e*a*c,m=o*a*u,l=m-p,d=l-i,C=p+i,g=C>0?d/C*100:0,D=u>0?e*c/u+i/(a*u||1):e*c;return{buy_price:s(e,2),sell_price:s(o,2),quantity:s(a,8),buy_fee_percent:s(n,4),sell_fee_percent:s(r,4),network_fee:s(i,4),invested:s(p,2),returned:s(m,2),profit:s(d,2),profit_before_network_fee:s(l,2),roi_percent:y(g),break_even_price:s(D,2),is_profit:d>=0}}import{z as R}from"zod";var ve=R.object({coin:R.string(),date:R.string().optional().describe("Single date DD-MM-YYYY. Use with investment snapshot."),days:R.union([R.number(),R.string()]).optional().describe("Chart range: 1, 7, 14, 30, 90, 180, 365, or max"),vs_currency:R.string().default("usd"),investment:R.number().default(1e3).describe("Hypothetical investment amount on the historical date")});function Ve(t){let e=t.match(/^(\d{2})-(\d{2})-(\d{4})$/);if(!e)throw new Error(`Invalid date format: ${t}. Expected DD-MM-YYYY (e.g. 15-01-2024)`);let[,o,a,n]=e,r=`${o}-${a}-${n}`,i=`${n}-${a}-${o}`,c=new Date(i);if(isNaN(c.getTime()))throw new Error(`Invalid date: ${t}`);let u=new Date;if(u.setHours(23,59,59,999),c>u)throw new Error(`Date cannot be in the future: ${t}`);return{formatted:r,iso:i}}function Je(t){if(t.length===0)return null;let e=t.map(m=>m.price),o=e[0],a=e[e.length-1],n=Math.max(...e),r=Math.min(...e),i=a-o,c=o>0?i/o*100:0,u=Math.max(1,Math.floor(t.length/12)),p=t.filter((m,l)=>l%u===0||l===t.length-1).map(m=>({time:new Date(m.timestamp).toISOString(),price:s(m.price,h("usd",m.price))}));return{start_price:s(o,h("usd",o)),end_price:s(a,h("usd",a)),high:s(n,h("usd",n)),low:s(r,h("usd",r)),change:s(i,4),change_percent:y(c),points:t.length,sample:p}}async function Ce(t){let e=(t.vs_currency??"usd").toLowerCase(),o=t.investment??1e3,a=f(t.coin);if(!t.date&&!t.days)throw new Error("Provide either date (DD-MM-YYYY) for a point-in-time price, or days (e.g. 7, 30, 90) for a range chart.");if(t.days!==void 0&&t.days!==null){let l=await ce(t.coin,e,t.days),d=Je(l.prices);return{mode:"range",coin:a,vs_currency:e,days:String(t.days),summary:d}}let{formatted:n}=Ve(t.date),r=await ie(t.coin,n,e),i=await ue(t.coin,e),c=r.price>0?o/r.price:0,u=c*i,p=u-o,m=o>0?p/o*100:0;return{mode:"point",coin:a,date:n,vs_currency:e,historical_price:s(r.price,h(e,r.price)),current_price:s(i,h(e,i)),price_change:s(i-r.price,4),price_change_percent:y(r.price>0?(i-r.price)/r.price*100:0),investment_then:{invested:o,coins_bought:s(c,8),worth_now:s(u,2),gain_loss:s(p,2),gain_loss_percent:y(m)}}}import{z as X}from"zod";var Se=X.object({query:X.string().describe("Name or symbol to search, e.g. pepe, solana, wif"),limit:X.number().int().min(1).max(50).default(10)});async function Pe(t){let e=t.limit??10,o=await se(t.query,e);return{query:t.query,count:o.length,results:o}}import{z as U}from"zod";var xe=U.object({vs_currency:U.string().default("usd"),limit:U.number().int().min(1).max(50).default(10),order:U.enum(["market_cap_desc","market_cap_asc","volume_desc","volume_asc","id_asc","id_desc"]).default("market_cap_desc")});async function Re(t){let e=(t.vs_currency??"usd").toLowerCase(),o=t.limit??10,a=t.order??"market_cap_desc",r=(await F(e,{perPage:o,order:a})).map(p=>({id:p.id,symbol:p.symbol,name:p.name,rank:p.market_cap_rank,price:s(p.current_price??0,h(e,p.current_price??0)),market_cap:p.market_cap?s(p.market_cap,0):null,total_volume:p.total_volume?s(p.total_volume,0):null,"24h_change":p.price_change_percentage_24h?y(p.price_change_percentage_24h):null})),i=r.filter(p=>p["24h_change"]!==null),c=[...i].sort((p,m)=>(m["24h_change"]??0)-(p["24h_change"]??0)).slice(0,3),u=[...i].sort((p,m)=>(p["24h_change"]??0)-(m["24h_change"]??0)).slice(0,3);return{vs_currency:e,order:a,count:r.length,coins:r,top_gainers:c,top_losers:u}}import{z as G}from"zod";var Ie=G.object({coins:G.union([G.array(G.string()).min(2).max(5),G.string().describe("Comma-separated symbols, e.g. btc,eth,sol")]).describe("2\u20135 coins to compare"),vs_currency:G.string().default("usd")});function We(t){let e=Array.isArray(t)?t:t.split(/[,\s]+/).map(a=>a.trim()).filter(Boolean),o=[...new Set(e.map(a=>a.toLowerCase()))];if(o.length<2||o.length>5)throw new Error("Provide between 2 and 5 coins to compare");return o}async function Ee(t){let e=(t.vs_currency??"usd").toLowerCase(),o=We(t.coins),a=o.map(f),n=await F(e,{ids:a,perPage:a.length}),r=new Map(n.map(m=>[m.id,m])),i=a.map((m,l)=>{let d=r.get(m);return d?{input:o[l],id:d.id,found:!0,symbol:d.symbol,name:d.name,rank:d.market_cap_rank,price:s(d.current_price??0,h(e,d.current_price??0)),market_cap:d.market_cap?s(d.market_cap,0):null,total_volume:d.total_volume?s(d.total_volume,0):null,"24h_change":d.price_change_percentage_24h?y(d.price_change_percentage_24h):null}:{input:o[l],id:m,found:!1}}),c=i.filter(m=>m.found),u=[...c].sort((m,l)=>(l["24h_change"]??-1/0)-(m["24h_change"]??-1/0))[0],p=[...c].sort((m,l)=>(l.market_cap??0)-(m.market_cap??0))[0];return{vs_currency:e,coins:i,highlights:{best_24h_performer:u?.id??null,largest_market_cap:p?.id??null}}}import{z as $}from"zod";var Qe=$.object({coin:$.string(),amount:$.number().positive()}),Ae=$.object({holdings:$.array(Qe).min(1).max(50).describe("List of { coin, amount } holdings"),vs_currency:$.string().default("usd")});async function Te(t){let e=(t.vs_currency??"usd").toLowerCase(),o=t.holdings,a=o.map(l=>f(l.coin)),n=[...new Set(a)],r=e==="pkr"?"usd":e,i=await P(n,[r]),c=null,u=null;if(e==="pkr"){let l=await q(1,"usdt");c=l.rate,u=l.source}let p=o.map((l,d)=>{let C=a[d],g=i[C]?.[r];if(g===void 0)return{coin:C,amount:l.amount,found:!1,value:null};let D=l.amount*g,Y=g;return e==="pkr"&&c!==null&&(D*=c,Y*=c),{coin:C,amount:l.amount,found:!0,unit_price:s(Y,h(e,Y)),value:s(D,2)}}),m=p.reduce((l,d)=>l+(d.found&&d.value!==null?d.value:0),0);return{vs_currency:e,holdings:p,total_value:s(m,2),missing:p.filter(l=>!l.found).map(l=>l.coin),...u?{rate_source:u}:{}}}import{z as Ne}from"zod";var Ge=Ne.object({include_fear_greed:Ne.boolean().default(!0)});async function $e(t={include_fear_greed:!0}){let e=t.include_fear_greed??!0,[o,a]=await Promise.all([pe(),e?le():Promise.resolve(null)]);return{source:"coingecko",count:o.length,coins:o,...a?{fear_greed:{source:"alternative.me",value:a.value,classification:a.classification,updated_at:a.updatedAt}}:{}}}var rt=[{name:"get_price",description:"Get live price of any cryptocurrency in any fiat, with market cap, volume, rank, ATH/ATL. Uses CoinGecko.",inputSchema:{type:"object",properties:{coin:{type:"string",description:"Coin id or symbol: btc, bitcoin, eth, sol, etc"},vs_currency:{type:"string",description:"Fiat: usd, eur, pkr, inr, gbp, etc",default:"usd"}},required:["coin"]}},{name:"convert",description:"Convert crypto amount to fiat/crypto. Optimized PKR via Binance P2P when requested.",inputSchema:{type:"object",properties:{amount:{type:"number"},from:{type:"string"},to:{type:"string",description:"usd, eur, pkr, btc, eth, etc"}},required:["amount","from","to"]}},{name:"gas_tracker",description:"Live gas fees for EVM chains (eth, bnb, polygon, arbitrum, base, optimism) with estimated transfer cost in native + USD.",inputSchema:{type:"object",properties:{chain:{type:"string",enum:["eth","bnb","polygon","arbitrum","base","optimism"],default:"eth"}}}},{name:"profit_calc",description:"Calculate crypto profit/loss, ROI, break-even. Supports exchange fees and fixed network/gas fee. No API needed.",inputSchema:{type:"object",properties:{buy_price:{type:"number"},sell_price:{type:"number"},quantity:{type:"number"},buy_fee_percent:{type:"number",default:0},sell_fee_percent:{type:"number",default:0},network_fee:{type:"number",default:0,description:"Fixed network fee in same unit as prices"}},required:["buy_price","sell_price","quantity"]}},{name:"historical_price",description:"Historical price on a date (DD-MM-YYYY) with investment snapshot, or a range chart via days (1/7/30/90/365/max).",inputSchema:{type:"object",properties:{coin:{type:"string"},date:{type:"string",description:"DD-MM-YYYY for point-in-time"},days:{type:["string","number"],description:"Range chart: 1, 7, 14, 30, 90, 180, 365, or max"},vs_currency:{type:"string",default:"usd"},investment:{type:"number",default:1e3,description:"Hypothetical investment on the historical date"}},required:["coin"]}},{name:"search_coin",description:"Search cryptocurrencies by name or symbol. Returns CoinGecko ids for use in other tools.",inputSchema:{type:"object",properties:{query:{type:"string"},limit:{type:"number",default:10}},required:["query"]}},{name:"top_coins",description:"Top cryptocurrencies by market cap or volume, with gainers/losers among the result set.",inputSchema:{type:"object",properties:{vs_currency:{type:"string",default:"usd"},limit:{type:"number",default:10},order:{type:"string",enum:["market_cap_desc","market_cap_asc","volume_desc","volume_asc"],default:"market_cap_desc"}}}},{name:"compare_coins",description:"Compare 2\u20135 cryptocurrencies side by side (price, mcap, volume, 24h change).",inputSchema:{type:"object",properties:{coins:{oneOf:[{type:"string"},{type:"array",items:{type:"string"}}],description:"Array or comma-separated list, e.g. btc,eth,sol"},vs_currency:{type:"string",default:"usd"}},required:["coins"]}},{name:"portfolio_value",description:"Value a portfolio of holdings in any fiat (batch pricing). Pass [{coin, amount}, ...].",inputSchema:{type:"object",properties:{holdings:{type:"array",items:{type:"object",properties:{coin:{type:"string"},amount:{type:"number"}},required:["coin","amount"]}},vs_currency:{type:"string",default:"usd"}},required:["holdings"]}},{name:"trending",description:"What's hot right now: CoinGecko trending coins plus Crypto Fear & Greed Index.",inputSchema:{type:"object",properties:{include_fear_greed:{type:"boolean",default:!0,description:"Include Alternative.me Fear & Greed Index (default true)"}}}}],nt=[{name:"market-overview",description:"Global crypto market snapshot: top coins, gainers/losers, and trending + fear/greed",arguments:[{name:"vs_currency",description:"Fiat currency (default usd)",required:!1},{name:"limit",description:"How many top coins (default 10)",required:!1}]},{name:"whats-trending",description:"Trending coins and Fear & Greed Index snapshot",arguments:[]},{name:"analyze-coin",description:"Analyze a coin: live price, 7d history, and optional conversion",arguments:[{name:"coin",description:"Coin symbol or id",required:!0},{name:"vs_currency",description:"Fiat currency (default usd)",required:!1}]},{name:"trade-pnl",description:"Walk through a profit/loss calculation for a completed trade",arguments:[{name:"buy_price",description:"Entry price",required:!0},{name:"sell_price",description:"Exit price",required:!0},{name:"quantity",description:"Amount traded",required:!0},{name:"fees",description:"Optional fee notes (percent or network)",required:!1}]},{name:"gas-check",description:"Check current gas and estimated transfer cost for an EVM chain",arguments:[{name:"chain",description:"eth, bnb, polygon, arbitrum, base, or optimism",required:!1}]}];function Z(t){return{content:[{type:"text",text:t}],isError:!0}}function S(t){return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}}function je(t){t.setRequestHandler(Ze,async()=>({tools:rt})),t.setRequestHandler(et,async()=>({prompts:nt})),t.setRequestHandler(tt,async e=>{let{name:o,arguments:a}=e.params,n=a??{};switch(o){case"market-overview":{let r=n.vs_currency??"usd",i=n.limit??"10";return{description:"Market overview workflow",messages:[{role:"user",content:{type:"text",text:`Give me a crypto market overview in ${r}. 1) Call top_coins with limit ${i}. 2) Call trending. Summarize leaders, gainers/losers, what's trending, and Fear & Greed tone.`}}]}}case"whats-trending":return{description:"Trending + sentiment workflow",messages:[{role:"user",content:{type:"text",text:"Call the trending tool. List the hottest coins and explain the Fear & Greed reading in plain language."}}]};case"analyze-coin":{let r=n.coin;if(!r)throw new Error("coin is required");let i=n.vs_currency??"usd";return{description:`Analyze ${r}`,messages:[{role:"user",content:{type:"text",text:`Analyze ${r} in ${i}. 1) Call get_price. 2) Call historical_price with days=7. 3) Summarize price, market cap, 24h move, and 7-day trend.`}}]}}case"trade-pnl":{let{buy_price:r,sell_price:i,quantity:c,fees:u}=n;if(!r||!i||!c)throw new Error("buy_price, sell_price, and quantity are required");return{description:"Trade P&L workflow",messages:[{role:"user",content:{type:"text",text:`Calculate P&L with profit_calc: buy_price=${r}, sell_price=${i}, quantity=${c}.${u?` Fee notes: ${u}.`:""} Explain profit, ROI, and break-even clearly.`}}]}}case"gas-check":{let r=n.chain??"eth";return{description:`Gas check for ${r}`,messages:[{role:"user",content:{type:"text",text:`Check gas on ${r} with gas_tracker. Report low/average/high gwei and estimated simple-transfer cost in USD. Advise whether fees look cheap or expensive.`}}]}}default:throw new Error(`Unknown prompt: ${o}`)}}),t.setRequestHandler(Xe,async e=>{let{name:o,arguments:a}=e.params;try{switch(o){case"get_price":return S(await he(de.parse(a??{})));case"convert":return S(await fe(ge.parse(a??{})));case"gas_tracker":return S(await ye(be.parse(a??{})));case"profit_calc":return S(ke(we.parse(a??{})));case"historical_price":return S(await Ce(ve.parse(a??{})));case"search_coin":return S(await Pe(Se.parse(a??{})));case"top_coins":return S(await Re(xe.parse(a??{})));case"compare_coins":return S(await Ee(Ie.parse(a??{})));case"portfolio_value":return S(await Te(Ae.parse(a??{})));case"trending":return S(await $e(Ge.parse(a??{})));default:return Z(`Unknown tool: ${o}`)}}catch(n){if(n instanceof w&&n.retryable)return Z(`Rate limit hit: ${n.message}. Wait a few seconds and retry.`);let r=n instanceof Error?n.message:"Unknown error occurred";return Z(r)}})}var Oe={name:"mcp-crypto-toolkit",version:"1.3.0",mcpName:"io.github.nad33mahm3d/mcp-crypto-toolkit",description:"Live crypto prices, conversion, gas tracker, portfolio tools and calculators for AI agents - MCP Server",type:"module",bin:{"mcp-crypto-toolkit":"dist/index.js"},files:["dist","README.md","LICENSE","CHANGELOG.md"],main:"./dist/index.js",exports:{".":"./dist/index.js"},scripts:{build:"tsup",dev:"tsup --watch",start:"node dist/index.js","start:stdio":"node dist/index.js","start:http":"node dist/http.js","dev:cf":"wrangler dev","deploy:cf":"wrangler deploy",test:"vitest run","test:watch":"vitest",inspector:"npx @modelcontextprotocol/inspector dist/index.js",prepack:"npm run build","pack:check":"npm pack --dry-run"},keywords:["mcp","mcp-server","crypto","bitcoin","ethereum","coingecko","gas","portfolio","claude","cursor","ai-agent"],license:"MIT",author:"Nadeem Ahmed <ebox.nadeem@gmail.com>",homepage:"https://github.com/nad33mahm3d/mcp-crypto-toolkit#readme",bugs:{url:"https://github.com/nad33mahm3d/mcp-crypto-toolkit/issues"},repository:{type:"git",url:"https://github.com/nad33mahm3d/mcp-crypto-toolkit.git"},publishConfig:{access:"public"},engines:{node:">=20"},dependencies:{"@modelcontextprotocol/sdk":"^1.0.0",zod:"^4.5.4"},devDependencies:{"@types/node":"^26.4.0",tsup:"^8.0.0",typescript:"^7.0.2",vitest:"^4.1.11",wrangler:"^4.129.0"}};var H=Oe.version,K="mcp-crypto-toolkit",Fe="Live crypto prices, conversion, gas tracker, portfolio tools and calculators for AI agents";function ee(){let t=new at({name:K,version:H,description:Fe},{capabilities:{tools:{},prompts:{}}});return je(t),t}var De=Number(process.env.PORT??3e3),j={"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"content-type, accept, mcp-session-id, mcp-protocol-version","Access-Control-Expose-Headers":"mcp-session-id"};function it(t){return new Promise((e,o)=>{let a=[];t.on("data",n=>a.push(n)),t.on("end",()=>e(Buffer.concat(a).toString("utf8"))),t.on("error",o)})}function ct(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.method=="string"&&!("id"in e)}var ut=st(async(t,e)=>{try{let o=t.url?.split("?")[0]??"/";if(t.method==="OPTIONS"&&o.startsWith("/mcp")){e.writeHead(204,{...j,"Access-Control-Max-Age":"86400"}),e.end();return}if(t.method==="GET"&&(o==="/"||o==="/health")){let a=JSON.stringify({ok:!0,name:K,version:H,mcp:"/mcp"});e.writeHead(200,{"Content-Type":"application/json","Content-Length":Buffer.byteLength(a),...j}),e.end(a);return}if(o.startsWith("/mcp")){if(t.method==="POST"){let r=await it(t),i;try{i=r?JSON.parse(r):void 0}catch{let p=JSON.stringify({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"},id:null});e.writeHead(400,{"Content-Type":"application/json","Content-Length":Buffer.byteLength(p),...j}),e.end(p);return}if(ct(i)){let p=`{"ok":true}
|
|
3
|
+
`;e.writeHead(202,{"Content-Type":"application/json","Content-Length":Buffer.byteLength(p),...j}),e.end(p);return}let c=ee(),u=new Me({sessionIdGenerator:void 0,enableJsonResponse:!0});e.on("close",()=>{u.close(),c.close()}),await c.connect(u),await u.handleRequest(t,e,i);return}let a=ee(),n=new Me({sessionIdGenerator:void 0,enableJsonResponse:!0});e.on("close",()=>{n.close(),a.close()}),await a.connect(n),await n.handleRequest(t,e);return}e.writeHead(404,{"Content-Type":"text/plain",...j}),e.end("Not found. Use POST /mcp or GET /health")}catch(o){console.error("HTTP error:",o),e.headersSent||(e.writeHead(500,{"Content-Type":"text/plain",...j}),e.end("Internal server error"))}});ut.listen(De,()=>{console.error(`mcp-crypto-toolkit HTTP listening on http://localhost:${De}/mcp`)});
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Server as He}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Be}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as Me,ListToolsRequestSchema as ze,ListPromptsRequestSchema as Le,GetPromptRequestSchema as Ue}from"@modelcontextprotocol/sdk/types.js";var Q={btc:"bitcoin",bitcoin:"bitcoin",eth:"ethereum",ethereum:"ethereum",usdt:"tether",tether:"tether",bnb:"binancecoin",binancecoin:"binancecoin",sol:"solana",solana:"solana",xrp:"ripple",ripple:"ripple",usdc:"usd-coin","usd-coin":"usd-coin",ada:"cardano",cardano:"cardano",avax:"avalanche-2",avalanche:"avalanche-2",doge:"dogecoin",dogecoin:"dogecoin",dot:"polkadot",polkadot:"polkadot",trx:"tron",tron:"tron",link:"chainlink",chainlink:"chainlink",matic:"matic-network",polygon:"matic-network","matic-network":"matic-network",shib:"shiba-inu","shiba-inu":"shiba-inu",ltc:"litecoin",litecoin:"litecoin",bch:"bitcoin-cash","bitcoin-cash":"bitcoin-cash",uni:"uniswap",uniswap:"uniswap",atom:"cosmos",cosmos:"cosmos",xlm:"stellar",stellar:"stellar",xmr:"monero",monero:"monero",etc:"ethereum-classic","ethereum-classic":"ethereum-classic",fil:"filecoin",filecoin:"filecoin",apt:"aptos",aptos:"aptos",arb:"arbitrum",arbitrum:"arbitrum",op:"optimism",optimism:"optimism",near:"near",vet:"vechain",vechain:"vechain",icp:"internet-computer",hbar:"hedera-hashgraph",algo:"algorand",algorand:"algorand",qnt:"quant-network",eos:"eos",aave:"aave",grt:"the-graph",sand:"the-sandbox",mana:"decentraland",axs:"axie-infinity",theta:"theta-token",ftm:"fantom",fantom:"fantom",xtz:"tezos",tezos:"tezos",rune:"thorchain",snx:"havven",crv:"curve-dao-token",mkr:"maker",maker:"maker",comp:"compound-governance-token",ldo:"lido-dao",sui:"sui",sei:"sei-network",inj:"injective-protocol",tia:"celestia",pepe:"pepe",wif:"dogwifcoin",bonk:"bonk",floki:"floki",render:"render-token",rndr:"render-token",fet:"fetch-ai",imx:"immutable-x",gala:"gala",enj:"enjincoin",bat:"basic-attention-token",zec:"zcash",dash:"dash",neo:"neo",kcs:"kucoin-shares",okb:"okb",ton:"the-open-network",kas:"kaspa",stx:"blockstack",rpl:"rocket-pool",blur:"blur",pendle:"pendle",jup:"jupiter-exchange-solana",pyth:"pyth-network",wld:"worldcoin-wld",strk:"starknet",ethfi:"ether-fi",ena:"ethena",ondo:"ondo-finance",not:"notcoin",bome:"book-of-meme",ar:"arweave",flow:"flow",mina:"mina-protocol",ro:"ronin",ronin:"ronin",egld:"elrond-erd-2",egld_m:"elrond-erd-2",cake:"pancakeswap-token","1inch":"1inch",ens:"ethereum-name-service",chz:"chiliz",hot:"holotoken",zil:"zilliqa",icx:"icon",waves:"waves",kava:"kava",celo:"celo",rose:"oasis-network",one:"harmony",iota:"iota",xem:"nem",sc:"siacoin",dcr:"decred",rvn:"ravencoin",zen:"zencash",ont:"ontology",qtum:"qtum",btt:"bittorrent",hnt:"helium",lrc:"loopring",ankr:"ankr",omg:"omisego",storj:"storj",skl:"skale",celr:"celer-network",coti:"coti",ren:"republic-protocol",band:"band-protocol",ocean:"ocean-protocol",api3:"api3",nmr:"numeraire",bal:"balancer",yfi:"yearn-finance",sushi:"sushi",dydx:"dydx-chain",gmx:"gmx",magic:"magic",rdnt:"radiant-capital",joe:"joe",woo:"woo-network",perp:"perpetual-protocol",lqty:"liquity",cvx:"convex-finance",frax:"frax",fxn:"fxn",usdd:"usdd",tusd:"true-usd",dai:"dai",frax_usd:"frax"};var X={eth:1,bnb:56,polygon:137,arbitrum:42161,base:8453,optimism:10},Z={eth:"ethereum",bnb:"binancecoin",polygon:"matic-network",arbitrum:"ethereum",base:"ethereum",optimism:"ethereum"},K=21e3,ee={eth:{url:"https://api.etherscan.io/api",apiKeyEnv:"ETHERSCAN_API_KEY"},bnb:{url:"https://api.bscscan.com/api",apiKeyEnv:"BSCSCAN_API_KEY"},polygon:{url:"https://api.polygonscan.com/api",apiKeyEnv:"POLYGONSCAN_API_KEY"},arbitrum:{url:"https://api.arbiscan.io/api",apiKeyEnv:"ARBISCAN_API_KEY"},base:{url:"https://api.basescan.org/api",apiKeyEnv:"BASESCAN_API_KEY"},optimism:{url:"https://api-optimistic.etherscan.io/api",apiKeyEnv:"OPTIMISM_API_KEY"}},R="mcp-crypto-toolkit/1.0.0 (https://github.com/nad33mahm3d/mcp-crypto-toolkit)";var E=class{store=new Map;timers=new Map;get(t){let o=this.store.get(t);if(o){if(Date.now()>o.expiresAt){this.delete(t);return}return o.value}}set(t,o,a){let n=this.timers.get(t);n&&clearTimeout(n),this.store.set(t,{value:o,expiresAt:Date.now()+a});let e=setTimeout(()=>{this.delete(t)},a);e.unref?.(),this.timers.set(t,e)}clear(){for(let t of this.timers.values())clearTimeout(t);this.timers.clear(),this.store.clear()}delete(t){let o=this.timers.get(t);o&&(clearTimeout(o),this.timers.delete(t)),this.store.delete(t)}},I=new E,Y=new E,N=new E,O=new E,k={PRICE:6e4,GAS:15e3,HISTORICAL:36e5,PKR:3e5,SEARCH:12e4};var G="https://api.coingecko.com/api/v3",b=class extends Error{constructor(o,a,n=!1){super(o);this.status=a;this.retryable=n;this.name="CoinGeckoError"}status;retryable};function f(r){let t=r.toLowerCase().trim();return Q[t]??t}function Ie(){let r=process.env.COINGECKO_API_KEY?.trim();return r?r.startsWith("CG-")&&process.env.COINGECKO_PRO==="1"?{"x-cg-pro-api-key":r}:{"x-cg-demo-api-key":r}:{}}async function q(r,t={},o=3){let a;for(let n=0;n<o;n++)try{let e=await fetch(r,{...t,headers:{Accept:"application/json","User-Agent":R,...Ie(),...t.headers??{}}});if(e.status===429){let i=Math.min(1e3*2**n,1e4);if(n<o-1){await te(i);continue}throw new b("CoinGecko rate limit exceeded (429). Wait a few seconds, or set COINGECKO_API_KEY for higher limits.",429,!0)}if(!e.ok)throw new b(`CoinGecko API error: ${e.status} ${e.statusText}`,e.status,e.status>=500);return e}catch(e){if(e instanceof b)throw e;a=e instanceof Error?e:new Error(String(e)),n<o-1&&await te(1e3*2**n)}throw a??new Error("Fetch failed after retries")}async function P(r,t){let o=[...new Set(r.map(f))],a=[...new Set(t.map(p=>p.toLowerCase()))],n=`simple:${o.sort().join(",")}:${a.sort().join(",")}`,e=I.get(n);if(e)return e;let i=new URLSearchParams({ids:o.join(","),vs_currencies:a.join(","),include_24hr_change:"true",include_last_updated_at:"true"}),u=await(await q(`${G}/simple/price?${i}`)).json();return I.set(n,u,k.PRICE),u}async function S(r,t){let o=f(r),a=t.toLowerCase(),e=(await P([o],[a]))[o];if(!e)throw new b(`Coin not found: ${r} (id: ${o})`);let i=e[a];if(i===void 0)throw new b(`Currency not supported: ${t}`);let c=`${a}_24h_change`,u="last_updated_at";return{id:o,price:Number(i),change24h:e[c]!==void 0?Number(e[c]):void 0,lastUpdated:e[u]!==void 0?new Date(Number(e[u])*1e3).toISOString():new Date().toISOString()}}async function j(r,t={}){let o=r.toLowerCase(),a=t.perPage??10,n=t.page??1,e=t.order??"market_cap_desc",i=t.ids?.map(f),c=`markets:${o}:${i?.sort().join(",")??"all"}:${a}:${n}:${e}`,u=I.get(c);if(u)return u;let p=new URLSearchParams({vs_currency:o,order:e,per_page:String(a),page:String(n),sparkline:"false",price_change_percentage:"24h"});i?.length&&p.set("ids",i.join(","));let l=await(await q(`${G}/coins/markets?${p}`)).json();return I.set(c,l,k.PRICE),l}async function H(r,t){let o=f(r),n=(await j(t,{ids:[o],perPage:1}))[0];if(!n)throw new b(`Coin not found: ${r} (id: ${o})`);return n}async function re(r,t=10){let o=r.trim();if(!o)return[];let a=`search:${o.toLowerCase()}:${t}`,n=I.get(a);if(n)return n;let c=((await(await q(`${G}/search?query=${encodeURIComponent(o)}`)).json()).coins??[]).slice(0,t).map(u=>({id:u.id,name:u.name,symbol:u.symbol,market_cap_rank:u.market_cap_rank}));return I.set(a,c,k.PRICE),c}async function ne(r,t,o){let a=f(r),n=o.toLowerCase(),e=`hist:${a}:${t}:${n}`,i=N.get(e);if(i)return i;let u=await(await q(`${G}/coins/${a}/history?date=${encodeURIComponent(t)}&localization=false`)).json();if(u.error||!u.market_data?.current_price?.[n])throw new b(u.error??`Historical price not found for ${r} on ${t}`);let p={id:a,date:t,price:u.market_data.current_price[n],vsCurrency:n};return N.set(e,p,k.HISTORICAL),p}async function oe(r,t,o){let a=f(r),n=t.toLowerCase(),e=String(o),i=`chart:${a}:${n}:${e}`,c=N.get(i);if(c)return c;let u=new URLSearchParams({vs_currency:n,days:e}),l=((await(await q(`${G}/coins/${a}/market_chart?${u}`)).json()).prices??[]).map(([v,h])=>({timestamp:v,price:h})),d={id:a,vs:n,days:e,prices:l};return N.set(i,d,k.HISTORICAL),d}async function ae(r,t){return(await S(r,t)).price}function te(r){return new Promise(t=>setTimeout(t,r))}function s(r,t=2){return Number.isFinite(r)?Number(r.toFixed(t)):0}function _(r,t=2){return Number.isFinite(r)?Number(r.toFixed(t)):0}function g(r,t){return r==="btc"||r==="eth"?8:t>=1?2:t>=.01?4:8}import{z as B}from"zod";async function F(){let r=O.get("usdt-pkr");if(r)return r;try{let o={rate:await Ae(),source:"binance_p2p",lastUpdated:new Date().toISOString()};return O.set("usdt-pkr",o,k.PKR),o}catch{let o={rate:await Ee(),source:"coingecko",lastUpdated:new Date().toISOString()};return O.set("usdt-pkr",o,k.PKR),o}}async function Ae(){let r=await fetch("https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":R},body:JSON.stringify({asset:"USDT",fiat:"PKR",merchantCheck:!1,page:1,payTypes:[],publisherType:null,rows:10,tradeType:"BUY"})});if(!r.ok)throw new Error(`Binance P2P API error: ${r.status}`);let o=(await r.json()).data?.slice(0,5)??[];if(o.length===0)throw new Error("No Binance P2P ads found for USDT/PKR");let a=o.map(e=>parseFloat(e.adv.price)).filter(e=>!isNaN(e));if(a.length===0)throw new Error("Invalid Binance P2P price data");let n=a.reduce((e,i)=>e+i,0)/a.length;return Number(n.toFixed(2))}async function Ee(){let t=(await P(["tether"],["pkr"])).tether?.pkr;if(t===void 0)throw new b("Could not fetch USDT/PKR rate from CoinGecko");return Number(t.toFixed(2))}async function M(r,t){let o=t.toLowerCase();if(o==="pkr")return{converted:r,rate:1,source:"direct"};if(o==="usdt"||o==="tether"){let u=await F();return{converted:Number((r*u.rate).toFixed(2)),rate:u.rate,source:u.source}}let a=f(o),e=(await P([a],["usd"]))[a]?.usd;if(e===void 0)throw new b(`Could not get USD price for ${t}`);let i=r*e,c=await F();return{converted:Number((i*c.rate).toFixed(2)),rate:Number((e*c.rate).toFixed(2)),source:`coingecko_usd + ${c.source}`}}async function se(r,t){let o=t.toLowerCase();if(o==="pkr")return{converted:r,rate:1,source:"direct"};let a=await F(),n=r/a.rate;if(o==="usdt"||o==="tether"||o==="usd")return{converted:Number(n.toFixed(6)),rate:Number((1/a.rate).toFixed(8)),source:a.source};let e=f(o),c=(await P([e],["usd"]))[e]?.usd;if(!c)throw new b(`Could not get USD price for ${t}`);let u=n/c;return{converted:Number(u.toFixed(8)),rate:Number((a.rate/c).toFixed(2)),source:`coingecko_usd + ${a.source}`}}var ie=B.object({coin:B.string().describe("Coin id or symbol: btc, bitcoin, eth, sol, etc"),vs_currency:B.string().default("usd").describe("Fiat: usd, eur, pkr, inr, gbp, etc")});async function ce(r){let{coin:t,vs_currency:o}=r,a=o.toLowerCase();if(a==="pkr"){let n=await F(),e=await H(t,"usd"),i=(e.current_price??0)*n.rate;return{coin:e.id,symbol:e.symbol,name:e.name,price:s(i,2),vs_currency:"pkr","24h_change":e.price_change_percentage_24h?_(e.price_change_percentage_24h):null,market_cap:e.market_cap?s(e.market_cap*n.rate,0):null,market_cap_rank:e.market_cap_rank,total_volume:e.total_volume?s(e.total_volume*n.rate,0):null,high_24h:e.high_24h?s(e.high_24h*n.rate,2):null,low_24h:e.low_24h?s(e.low_24h*n.rate,2):null,ath:e.ath?s(e.ath*n.rate,2):null,ath_change_percent:e.ath_change_percentage?_(e.ath_change_percentage):null,usd_price:s(e.current_price,2),rate_source:n.source,last_updated:e.last_updated??new Date().toISOString()}}try{let n=await H(t,a),e=n.current_price??0;return{coin:n.id,symbol:n.symbol,name:n.name,price:s(e,g(a,e)),vs_currency:a,"24h_change":n.price_change_percentage_24h?_(n.price_change_percentage_24h):null,market_cap:n.market_cap?s(n.market_cap,0):null,market_cap_rank:n.market_cap_rank,total_volume:n.total_volume?s(n.total_volume,0):null,high_24h:n.high_24h?s(n.high_24h,g(a,n.high_24h)):null,low_24h:n.low_24h?s(n.low_24h,g(a,n.low_24h)):null,ath:n.ath?s(n.ath,g(a,n.ath)):null,ath_change_percent:n.ath_change_percentage?_(n.ath_change_percentage):null,atl:n.atl?s(n.atl,g(a,n.atl)):null,last_updated:n.last_updated??new Date().toISOString()}}catch{let n=await S(t,a);return{coin:n.id,price:s(n.price,g(a,n.price)),vs_currency:a,"24h_change":n.change24h?_(n.change24h):null,last_updated:n.lastUpdated??new Date().toISOString()}}}import{z}from"zod";var ue=z.object({amount:z.number(),from:z.string(),to:z.string().describe("usd, pkr, btc, eth, etc")});async function pe(r){let{amount:t,from:o,to:a}=r,n=o.toLowerCase(),e=a.toLowerCase();if(n===e)return{amount:t,from:n,to:e,converted:t,rate:1,source:"direct"};if(e==="pkr"){let h=await M(t,n);return{amount:t,from:f(n),to:"pkr",converted:h.converted,rate:h.rate,source:h.source}}if(n==="pkr"){let h=await se(t,e);return{amount:t,from:"pkr",to:f(e),converted:h.converted,rate:h.rate,source:h.source}}let i=f(n),c=f(e),u=new Set(["usd","eur","gbp","jpy","inr","aud","cad","chf","cny"]);if(u.has(e)){let h=await S(n,e);return{amount:t,from:i,to:e,converted:s(t*h.price,2),rate:s(h.price,2),source:"coingecko"}}if(u.has(n)){let h=await S(e,n);return{amount:t,from:n,to:c,converted:s(t/h.price,8),rate:s(1/h.price,8),source:"coingecko"}}let p=await P([i,c],["usd"]),m=p[i]?.usd,l=p[c]?.usd;if(!m||!l)throw new Error(`Could not convert ${o} to ${a}`);let d=m/l,v=t*d;return{amount:t,from:i,to:c,converted:s(v,8),rate:s(d,8),source:"coingecko"}}import{z as le}from"zod";var $e=["eth","bnb","polygon","arbitrum","base","optimism"],me=le.object({chain:le.enum($e).default("eth")});async function de(r){let t=r.chain,o=`gas:${t}`,a=Y.get(o);if(a)return a;let n,e=ee[t],i=e?process.env[e.apiKeyEnv]:void 0;if(e&&i)try{n=await Ne(t,e.url,i)}catch{}n||(n=await Ge(t));let c=await Te(n);return Y.set(o,c,k.GAS),c}function V(r){return r*K/1e9}async function Te(r){let t=Z[r.chain]??"ethereum",o=null;try{o=(await S(t,"usd")).price}catch{o=null}let a=V(r.low),n=V(r.average),e=V(r.high);return{...r,estimated_transfer:{gas_units:K,cost_native_low:s(a,8),cost_native_average:s(n,8),cost_native_high:s(e,8),cost_usd_low:o!==null?s(a*o,4):null,cost_usd_average:o!==null?s(n*o,4):null,cost_usd_high:o!==null?s(e*o,4):null,native_token:t}}}async function Ne(r,t,o){let a=`${t}?module=gastracker&action=gasoracle&apikey=${o}`,n=await fetch(a,{headers:{"User-Agent":R,Accept:"application/json"}});if(!n.ok)throw new Error(`Scan API error: ${n.status}`);let e=await n.json();if(e.status!=="1"||!e.result)throw new Error("Invalid scan gas oracle response");return{chain:r,low:s(parseFloat(e.result.SafeGasPrice??"0"),2),average:s(parseFloat(e.result.ProposeGasPrice??"0"),2),high:s(parseFloat(e.result.FastGasPrice??"0"),2),baseFee:e.result.suggestBaseFee?s(parseFloat(e.result.suggestBaseFee),2):null,unit:"gwei",last_updated:new Date().toISOString(),source:`${r}scan`}}async function Ge(r){let t=X[r];if(!t)throw new Error(`Unsupported chain: ${r}`);try{let o=await fetch(`https://api.blocknative.com/gasprices/blockprices?chainid=${t}`,{headers:{Accept:"application/json","User-Agent":R}});if(o.ok){let n=(await o.json()).blockPrices?.[0],e=n?.estimatedPrices??[];if(e.length>0){let i=[...e].sort((l,d)=>l.price-d.price),c=i[0]?.price??0,u=i[i.length-1]?.price??0,p=i[Math.floor(i.length/2)]?.price??c,m=n?.baseFeePerGas?n.baseFeePerGas/1e9:null;return{chain:r,low:s(c,2),average:s(p,2),high:s(u,2),baseFee:m!==null?s(m,2):null,unit:"gwei",last_updated:new Date().toISOString(),source:"blocknative"}}}}catch{}return qe(r)}function qe(r){let t={eth:{low:15,average:25,high:40},bnb:{low:3,average:5,high:8},polygon:{low:30,average:50,high:80},arbitrum:{low:.1,average:.2,high:.5},base:{low:.01,average:.05,high:.1},optimism:{low:.01,average:.05,high:.1}},o=t[r]??t.eth;return{chain:r,low:o.low,average:o.average,high:o.high,baseFee:null,unit:"gwei",last_updated:new Date().toISOString(),source:"static_estimate"}}import{z as A}from"zod";var he=A.object({buy_price:A.number(),sell_price:A.number(),quantity:A.number(),buy_fee_percent:A.number().default(0),sell_fee_percent:A.number().default(0),network_fee:A.number().default(0).describe("Fixed network/gas fee in the same unit as prices (e.g. USD)")});function ge(r){let t=r.buy_price,o=r.sell_price,a=r.quantity,n=r.buy_fee_percent??0,e=r.sell_fee_percent??0,i=r.network_fee??0,c=1+n/100,u=1-e/100,p=t*a*c,m=o*a*u,l=m-p,d=l-i,v=p+i,h=v>0?d/v*100:0,D=u>0?t*c/u+i/(a*u||1):t*c;return{buy_price:s(t,2),sell_price:s(o,2),quantity:s(a,8),buy_fee_percent:s(n,4),sell_fee_percent:s(e,4),network_fee:s(i,4),invested:s(p,2),returned:s(m,2),profit:s(d,2),profit_before_network_fee:s(l,2),roi_percent:_(h),break_even_price:s(D,2),is_profit:d>=0}}import{z as x}from"zod";var fe=x.object({coin:x.string(),date:x.string().optional().describe("Single date DD-MM-YYYY. Use with investment snapshot."),days:x.union([x.number(),x.string()]).optional().describe("Chart range: 1, 7, 14, 30, 90, 180, 365, or max"),vs_currency:x.string().default("usd"),investment:x.number().default(1e3).describe("Hypothetical investment amount on the historical date")});function je(r){let t=r.match(/^(\d{2})-(\d{2})-(\d{4})$/);if(!t)throw new Error(`Invalid date format: ${r}. Expected DD-MM-YYYY (e.g. 15-01-2024)`);let[,o,a,n]=t,e=`${o}-${a}-${n}`,i=`${n}-${a}-${o}`,c=new Date(i);if(isNaN(c.getTime()))throw new Error(`Invalid date: ${r}`);let u=new Date;if(u.setHours(23,59,59,999),c>u)throw new Error(`Date cannot be in the future: ${r}`);return{formatted:e,iso:i}}function Fe(r){if(r.length===0)return null;let t=r.map(m=>m.price),o=t[0],a=t[t.length-1],n=Math.max(...t),e=Math.min(...t),i=a-o,c=o>0?i/o*100:0,u=Math.max(1,Math.floor(r.length/12)),p=r.filter((m,l)=>l%u===0||l===r.length-1).map(m=>({time:new Date(m.timestamp).toISOString(),price:s(m.price,g("usd",m.price))}));return{start_price:s(o,g("usd",o)),end_price:s(a,g("usd",a)),high:s(n,g("usd",n)),low:s(e,g("usd",e)),change:s(i,4),change_percent:_(c),points:r.length,sample:p}}async function _e(r){let t=(r.vs_currency??"usd").toLowerCase(),o=r.investment??1e3,a=f(r.coin);if(!r.date&&!r.days)throw new Error("Provide either date (DD-MM-YYYY) for a point-in-time price, or days (e.g. 7, 30, 90) for a range chart.");if(r.days!==void 0&&r.days!==null){let l=await oe(r.coin,t,r.days),d=Fe(l.prices);return{mode:"range",coin:a,vs_currency:t,days:String(r.days),summary:d}}let{formatted:n}=je(r.date),e=await ne(r.coin,n,t),i=await ae(r.coin,t),c=e.price>0?o/e.price:0,u=c*i,p=u-o,m=o>0?p/o*100:0;return{mode:"point",coin:a,date:n,vs_currency:t,historical_price:s(e.price,g(t,e.price)),current_price:s(i,g(t,i)),price_change:s(i-e.price,4),price_change_percent:_(e.price>0?(i-e.price)/e.price*100:0),investment_then:{invested:o,coins_bought:s(c,8),worth_now:s(u,2),gain_loss:s(p,2),gain_loss_percent:_(m)}}}import{z as W}from"zod";var be=W.object({query:W.string().describe("Name or symbol to search, e.g. pepe, solana, wif"),limit:W.number().int().min(1).max(50).default(10)});async function ye(r){let t=r.limit??10,o=await re(r.query,t);return{query:r.query,count:o.length,results:o}}import{z as L}from"zod";var we=L.object({vs_currency:L.string().default("usd"),limit:L.number().int().min(1).max(50).default(10),order:L.enum(["market_cap_desc","market_cap_asc","volume_desc","volume_asc","id_asc","id_desc"]).default("market_cap_desc")});async function ke(r){let t=(r.vs_currency??"usd").toLowerCase(),o=r.limit??10,a=r.order??"market_cap_desc",e=(await j(t,{perPage:o,order:a})).map(p=>({id:p.id,symbol:p.symbol,name:p.name,rank:p.market_cap_rank,price:s(p.current_price??0,g(t,p.current_price??0)),market_cap:p.market_cap?s(p.market_cap,0):null,total_volume:p.total_volume?s(p.total_volume,0):null,"24h_change":p.price_change_percentage_24h?_(p.price_change_percentage_24h):null})),i=e.filter(p=>p["24h_change"]!==null),c=[...i].sort((p,m)=>(m["24h_change"]??0)-(p["24h_change"]??0)).slice(0,3),u=[...i].sort((p,m)=>(p["24h_change"]??0)-(m["24h_change"]??0)).slice(0,3);return{vs_currency:t,order:a,count:e.length,coins:e,top_gainers:c,top_losers:u}}import{z as $}from"zod";var ve=$.object({coins:$.union([$.array($.string()).min(2).max(5),$.string().describe("Comma-separated symbols, e.g. btc,eth,sol")]).describe("2\u20135 coins to compare"),vs_currency:$.string().default("usd")});function De(r){let t=Array.isArray(r)?r:r.split(/[,\s]+/).map(a=>a.trim()).filter(Boolean),o=[...new Set(t.map(a=>a.toLowerCase()))];if(o.length<2||o.length>5)throw new Error("Provide between 2 and 5 coins to compare");return o}async function Pe(r){let t=(r.vs_currency??"usd").toLowerCase(),o=De(r.coins),a=o.map(f),n=await j(t,{ids:a,perPage:a.length}),e=new Map(n.map(m=>[m.id,m])),i=a.map((m,l)=>{let d=e.get(m);return d?{input:o[l],id:d.id,found:!0,symbol:d.symbol,name:d.name,rank:d.market_cap_rank,price:s(d.current_price??0,g(t,d.current_price??0)),market_cap:d.market_cap?s(d.market_cap,0):null,total_volume:d.total_volume?s(d.total_volume,0):null,"24h_change":d.price_change_percentage_24h?_(d.price_change_percentage_24h):null}:{input:o[l],id:m,found:!1}}),c=i.filter(m=>m.found),u=[...c].sort((m,l)=>(l["24h_change"]??-1/0)-(m["24h_change"]??-1/0))[0],p=[...c].sort((m,l)=>(l.market_cap??0)-(m.market_cap??0))[0];return{vs_currency:t,coins:i,highlights:{best_24h_performer:u?.id??null,largest_market_cap:p?.id??null}}}import{z as T}from"zod";var Oe=T.object({coin:T.string(),amount:T.number().positive()}),Ce=T.object({holdings:T.array(Oe).min(1).max(50).describe("List of { coin, amount } holdings"),vs_currency:T.string().default("usd")});async function Se(r){let t=(r.vs_currency??"usd").toLowerCase(),o=r.holdings,a=o.map(l=>f(l.coin)),n=[...new Set(a)],e=t==="pkr"?"usd":t,i=await P(n,[e]),c=null,u=null;if(t==="pkr"){let l=await M(1,"usdt");c=l.rate,u=l.source}let p=o.map((l,d)=>{let v=a[d],h=i[v]?.[e];if(h===void 0)return{coin:v,amount:l.amount,found:!1,value:null};let D=l.amount*h,U=h;return t==="pkr"&&c!==null&&(D*=c,U*=c),{coin:v,amount:l.amount,found:!0,unit_price:s(U,g(t,U)),value:s(D,2)}}),m=p.reduce((l,d)=>l+(d.found&&d.value!==null?d.value:0),0);return{vs_currency:t,holdings:p,total_value:s(m,2),missing:p.filter(l=>!l.found).map(l=>l.coin),...u?{rate_source:u}:{}}}var Ke=[{name:"get_price",description:"Get live price of any cryptocurrency in any fiat, with market cap, volume, rank, ATH/ATL. Uses CoinGecko.",inputSchema:{type:"object",properties:{coin:{type:"string",description:"Coin id or symbol: btc, bitcoin, eth, sol, etc"},vs_currency:{type:"string",description:"Fiat: usd, eur, pkr, inr, gbp, etc",default:"usd"}},required:["coin"]}},{name:"convert",description:"Convert crypto amount to fiat/crypto. Optimized PKR via Binance P2P when requested.",inputSchema:{type:"object",properties:{amount:{type:"number"},from:{type:"string"},to:{type:"string",description:"usd, eur, pkr, btc, eth, etc"}},required:["amount","from","to"]}},{name:"gas_tracker",description:"Live gas fees for EVM chains (eth, bnb, polygon, arbitrum, base, optimism) with estimated transfer cost in native + USD.",inputSchema:{type:"object",properties:{chain:{type:"string",enum:["eth","bnb","polygon","arbitrum","base","optimism"],default:"eth"}}}},{name:"profit_calc",description:"Calculate crypto profit/loss, ROI, break-even. Supports exchange fees and fixed network/gas fee. No API needed.",inputSchema:{type:"object",properties:{buy_price:{type:"number"},sell_price:{type:"number"},quantity:{type:"number"},buy_fee_percent:{type:"number",default:0},sell_fee_percent:{type:"number",default:0},network_fee:{type:"number",default:0,description:"Fixed network fee in same unit as prices"}},required:["buy_price","sell_price","quantity"]}},{name:"historical_price",description:"Historical price on a date (DD-MM-YYYY) with investment snapshot, or a range chart via days (1/7/30/90/365/max).",inputSchema:{type:"object",properties:{coin:{type:"string"},date:{type:"string",description:"DD-MM-YYYY for point-in-time"},days:{description:"Range chart: 1, 7, 14, 30, 90, 180, 365, or max"},vs_currency:{type:"string",default:"usd"},investment:{type:"number",default:1e3,description:"Hypothetical investment on the historical date"}},required:["coin"]}},{name:"search_coin",description:"Search cryptocurrencies by name or symbol. Returns CoinGecko ids for use in other tools.",inputSchema:{type:"object",properties:{query:{type:"string"},limit:{type:"number",default:10}},required:["query"]}},{name:"top_coins",description:"Top cryptocurrencies by market cap or volume, with gainers/losers among the result set.",inputSchema:{type:"object",properties:{vs_currency:{type:"string",default:"usd"},limit:{type:"number",default:10},order:{type:"string",enum:["market_cap_desc","market_cap_asc","volume_desc","volume_asc"],default:"market_cap_desc"}}}},{name:"compare_coins",description:"Compare 2\u20135 cryptocurrencies side by side (price, mcap, volume, 24h change).",inputSchema:{type:"object",properties:{coins:{description:"Array or comma-separated list, e.g. btc,eth,sol"},vs_currency:{type:"string",default:"usd"}},required:["coins"]}},{name:"portfolio_value",description:"Value a portfolio of holdings in any fiat (batch pricing). Pass [{coin, amount}, ...].",inputSchema:{type:"object",properties:{holdings:{type:"array",items:{type:"object",properties:{coin:{type:"string"},amount:{type:"number"}},required:["coin","amount"]}},vs_currency:{type:"string",default:"usd"}},required:["holdings"]}}],Ye=[{name:"market-overview",description:"Global crypto market snapshot using top coins and gainers/losers",arguments:[{name:"vs_currency",description:"Fiat currency (default usd)",required:!1},{name:"limit",description:"How many top coins (default 10)",required:!1}]},{name:"analyze-coin",description:"Analyze a coin: live price, 7d history, and optional conversion",arguments:[{name:"coin",description:"Coin symbol or id",required:!0},{name:"vs_currency",description:"Fiat currency (default usd)",required:!1}]},{name:"trade-pnl",description:"Walk through a profit/loss calculation for a completed trade",arguments:[{name:"buy_price",description:"Entry price",required:!0},{name:"sell_price",description:"Exit price",required:!0},{name:"quantity",description:"Amount traded",required:!0},{name:"fees",description:"Optional fee notes (percent or network)",required:!1}]},{name:"gas-check",description:"Check current gas and estimated transfer cost for an EVM chain",arguments:[{name:"chain",description:"eth, bnb, polygon, arbitrum, base, or optimism",required:!1}]}];function J(r){return{content:[{type:"text",text:r}],isError:!0}}function C(r){return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}function xe(r){r.setRequestHandler(ze,async()=>({tools:Ke})),r.setRequestHandler(Le,async()=>({prompts:Ye})),r.setRequestHandler(Ue,async t=>{let{name:o,arguments:a}=t.params,n=a??{};switch(o){case"market-overview":{let e=n.vs_currency??"usd",i=n.limit??"10";return{description:"Market overview workflow",messages:[{role:"user",content:{type:"text",text:`Give me a crypto market overview in ${e}. Use the top_coins tool with limit ${i}. Summarize leaders, notable gainers/losers, and overall tone.`}}]}}case"analyze-coin":{let e=n.coin;if(!e)throw new Error("coin is required");let i=n.vs_currency??"usd";return{description:`Analyze ${e}`,messages:[{role:"user",content:{type:"text",text:`Analyze ${e} in ${i}. 1) Call get_price. 2) Call historical_price with days=7. 3) Summarize price, market cap, 24h move, and 7-day trend.`}}]}}case"trade-pnl":{let{buy_price:e,sell_price:i,quantity:c,fees:u}=n;if(!e||!i||!c)throw new Error("buy_price, sell_price, and quantity are required");return{description:"Trade P&L workflow",messages:[{role:"user",content:{type:"text",text:`Calculate P&L with profit_calc: buy_price=${e}, sell_price=${i}, quantity=${c}.${u?` Fee notes: ${u}.`:""} Explain profit, ROI, and break-even clearly.`}}]}}case"gas-check":{let e=n.chain??"eth";return{description:`Gas check for ${e}`,messages:[{role:"user",content:{type:"text",text:`Check gas on ${e} with gas_tracker. Report low/average/high gwei and estimated simple-transfer cost in USD. Advise whether fees look cheap or expensive.`}}]}}default:throw new Error(`Unknown prompt: ${o}`)}}),r.setRequestHandler(Me,async t=>{let{name:o,arguments:a}=t.params;try{switch(o){case"get_price":return C(await ce(ie.parse(a??{})));case"convert":return C(await pe(ue.parse(a??{})));case"gas_tracker":return C(await de(me.parse(a??{})));case"profit_calc":return C(ge(he.parse(a??{})));case"historical_price":return C(await _e(fe.parse(a??{})));case"search_coin":return C(await ye(be.parse(a??{})));case"top_coins":return C(await ke(we.parse(a??{})));case"compare_coins":return C(await Pe(ve.parse(a??{})));case"portfolio_value":return C(await Se(Ce.parse(a??{})));default:return J(`Unknown tool: ${o}`)}}catch(n){if(n instanceof b&&n.retryable)return J(`Rate limit hit: ${n.message}. Wait a few seconds and retry.`);let e=n instanceof Error?n.message:"Unknown error occurred";return J(e)}})}var Re=new He({name:"mcp-crypto-toolkit",version:"1.1.0",description:"Live crypto prices, conversion, gas tracker, portfolio tools and calculators for AI agents"},{capabilities:{tools:{},prompts:{}}});xe(Re);var Ve=new Be;await Re.connect(Ve);
|
|
2
|
+
import{StdioServerTransport as nt}from"@modelcontextprotocol/sdk/server/stdio.js";import{Server as rt}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as We,ListToolsRequestSchema as Je,ListPromptsRequestSchema as Qe,GetPromptRequestSchema as Xe}from"@modelcontextprotocol/sdk/types.js";var Q={btc:"bitcoin",bitcoin:"bitcoin",eth:"ethereum",ethereum:"ethereum",usdt:"tether",tether:"tether",bnb:"binancecoin",binancecoin:"binancecoin",sol:"solana",solana:"solana",xrp:"ripple",ripple:"ripple",usdc:"usd-coin","usd-coin":"usd-coin",ada:"cardano",cardano:"cardano",avax:"avalanche-2",avalanche:"avalanche-2",doge:"dogecoin",dogecoin:"dogecoin",dot:"polkadot",polkadot:"polkadot",trx:"tron",tron:"tron",link:"chainlink",chainlink:"chainlink",matic:"matic-network",polygon:"matic-network","matic-network":"matic-network",shib:"shiba-inu","shiba-inu":"shiba-inu",ltc:"litecoin",litecoin:"litecoin",bch:"bitcoin-cash","bitcoin-cash":"bitcoin-cash",uni:"uniswap",uniswap:"uniswap",atom:"cosmos",cosmos:"cosmos",xlm:"stellar",stellar:"stellar",xmr:"monero",monero:"monero",etc:"ethereum-classic","ethereum-classic":"ethereum-classic",fil:"filecoin",filecoin:"filecoin",apt:"aptos",aptos:"aptos",arb:"arbitrum",arbitrum:"arbitrum",op:"optimism",optimism:"optimism",near:"near",vet:"vechain",vechain:"vechain",icp:"internet-computer",hbar:"hedera-hashgraph",algo:"algorand",algorand:"algorand",qnt:"quant-network",eos:"eos",aave:"aave",grt:"the-graph",sand:"the-sandbox",mana:"decentraland",axs:"axie-infinity",theta:"theta-token",ftm:"fantom",fantom:"fantom",xtz:"tezos",tezos:"tezos",rune:"thorchain",snx:"havven",crv:"curve-dao-token",mkr:"maker",maker:"maker",comp:"compound-governance-token",ldo:"lido-dao",sui:"sui",sei:"sei-network",inj:"injective-protocol",tia:"celestia",pepe:"pepe",wif:"dogwifcoin",bonk:"bonk",floki:"floki",render:"render-token",rndr:"render-token",fet:"fetch-ai",imx:"immutable-x",gala:"gala",enj:"enjincoin",bat:"basic-attention-token",zec:"zcash",dash:"dash",neo:"neo",kcs:"kucoin-shares",okb:"okb",ton:"the-open-network",kas:"kaspa",stx:"blockstack",rpl:"rocket-pool",blur:"blur",pendle:"pendle",jup:"jupiter-exchange-solana",pyth:"pyth-network",wld:"worldcoin-wld",strk:"starknet",ethfi:"ether-fi",ena:"ethena",ondo:"ondo-finance",not:"notcoin",bome:"book-of-meme",ar:"arweave",flow:"flow",mina:"mina-protocol",ro:"ronin",ronin:"ronin",egld:"elrond-erd-2",egld_m:"elrond-erd-2",cake:"pancakeswap-token","1inch":"1inch",ens:"ethereum-name-service",chz:"chiliz",hot:"holotoken",zil:"zilliqa",icx:"icon",waves:"waves",kava:"kava",celo:"celo",rose:"oasis-network",one:"harmony",iota:"iota",xem:"nem",sc:"siacoin",dcr:"decred",rvn:"ravencoin",zen:"zencash",ont:"ontology",qtum:"qtum",btt:"bittorrent",hnt:"helium",lrc:"loopring",ankr:"ankr",omg:"omisego",storj:"storj",skl:"skale",celr:"celer-network",coti:"coti",ren:"republic-protocol",band:"band-protocol",ocean:"ocean-protocol",api3:"api3",nmr:"numeraire",bal:"balancer",yfi:"yearn-finance",sushi:"sushi",dydx:"dydx-chain",gmx:"gmx",magic:"magic",rdnt:"radiant-capital",joe:"joe",woo:"woo-network",perp:"perpetual-protocol",lqty:"liquity",cvx:"convex-finance",frax:"frax",fxn:"fxn",usdd:"usdd",tusd:"true-usd",dai:"dai",frax_usd:"frax"};var X={eth:1,bnb:56,polygon:137,arbitrum:42161,base:8453,optimism:10},Z={eth:"ethereum",bnb:"binancecoin",polygon:"matic-network",arbitrum:"ethereum",base:"ethereum",optimism:"ethereum"},K=21e3,ee={eth:{url:"https://api.etherscan.io/api",apiKeyEnv:"ETHERSCAN_API_KEY"},bnb:{url:"https://api.bscscan.com/api",apiKeyEnv:"BSCSCAN_API_KEY"},polygon:{url:"https://api.polygonscan.com/api",apiKeyEnv:"POLYGONSCAN_API_KEY"},arbitrum:{url:"https://api.arbiscan.io/api",apiKeyEnv:"ARBISCAN_API_KEY"},base:{url:"https://api.basescan.org/api",apiKeyEnv:"BASESCAN_API_KEY"},optimism:{url:"https://api-optimistic.etherscan.io/api",apiKeyEnv:"OPTIMISM_API_KEY"}},R="mcp-crypto-toolkit/1.0.0 (https://github.com/nad33mahm3d/mcp-crypto-toolkit)";var T=class{store=new Map;timers=new Map;get(r){let o=this.store.get(r);if(o){if(Date.now()>o.expiresAt){this.delete(r);return}return o.value}}set(r,o,a){let n=this.timers.get(r);n&&clearTimeout(n),this.store.set(r,{value:o,expiresAt:Date.now()+a});let e=setTimeout(()=>{this.delete(r)},a);e.unref?.(),this.timers.set(r,e)}clear(){for(let r of this.timers.values())clearTimeout(r);this.timers.clear(),this.store.clear()}delete(r){let o=this.timers.get(r);o&&(clearTimeout(o),this.timers.delete(r)),this.store.delete(r)}},v=new T,Y=new T,j=new T,O=new T,k={PRICE:6e4,GAS:15e3,HISTORICAL:36e5,PKR:3e5,SEARCH:12e4,TRENDING:18e4};var N="https://api.coingecko.com/api/v3",w=class extends Error{constructor(o,a,n=!1){super(o);this.status=a;this.retryable=n;this.name="CoinGeckoError"}status;retryable};function f(t){let r=t.toLowerCase().trim();return Q[r]??r}function qe(){let t=process.env.COINGECKO_API_KEY?.trim();return t?t.startsWith("CG-")&&process.env.COINGECKO_PRO==="1"?{"x-cg-pro-api-key":t}:{"x-cg-demo-api-key":t}:{}}async function E(t,r={},o=3){let a;for(let n=0;n<o;n++)try{let e=await fetch(t,{...r,headers:{Accept:"application/json","User-Agent":R,...qe(),...r.headers??{}}});if(e.status===429){let i=Math.min(1e3*2**n,1e4);if(n<o-1){await te(i);continue}throw new w("CoinGecko rate limit exceeded (429). Wait a few seconds, or set COINGECKO_API_KEY for higher limits.",429,!0)}if(!e.ok)throw new w(`CoinGecko API error: ${e.status} ${e.statusText}`,e.status,e.status>=500);return e}catch(e){if(e instanceof w)throw e;a=e instanceof Error?e:new Error(String(e)),n<o-1&&await te(1e3*2**n)}throw a??new Error("Fetch failed after retries")}async function S(t,r){let o=[...new Set(t.map(f))],a=[...new Set(r.map(p=>p.toLowerCase()))],n=`simple:${o.sort().join(",")}:${a.sort().join(",")}`,e=v.get(n);if(e)return e;let i=new URLSearchParams({ids:o.join(","),vs_currencies:a.join(","),include_24hr_change:"true",include_last_updated_at:"true"}),u=await(await E(`${N}/simple/price?${i}`)).json();return v.set(n,u,k.PRICE),u}async function P(t,r){let o=f(t),a=r.toLowerCase(),e=(await S([o],[a]))[o];if(!e)throw new w(`Coin not found: ${t} (id: ${o})`);let i=e[a];if(i===void 0)throw new w(`Currency not supported: ${r}`);let c=`${a}_24h_change`,u="last_updated_at";return{id:o,price:Number(i),change24h:e[c]!==void 0?Number(e[c]):void 0,lastUpdated:e[u]!==void 0?new Date(Number(e[u])*1e3).toISOString():new Date().toISOString()}}async function F(t,r={}){let o=t.toLowerCase(),a=r.perPage??10,n=r.page??1,e=r.order??"market_cap_desc",i=r.ids?.map(f),c=`markets:${o}:${i?.sort().join(",")??"all"}:${a}:${n}:${e}`,u=v.get(c);if(u)return u;let p=new URLSearchParams({vs_currency:o,order:e,per_page:String(a),page:String(n),sparkline:"false",price_change_percentage:"24h"});i?.length&&p.set("ids",i.join(","));let l=await(await E(`${N}/coins/markets?${p}`)).json();return v.set(c,l,k.PRICE),l}async function H(t,r){let o=f(t),n=(await F(r,{ids:[o],perPage:1}))[0];if(!n)throw new w(`Coin not found: ${t} (id: ${o})`);return n}async function re(t,r=10){let o=t.trim();if(!o)return[];let a=`search:${o.toLowerCase()}:${r}`,n=v.get(a);if(n)return n;let c=((await(await E(`${N}/search?query=${encodeURIComponent(o)}`)).json()).coins??[]).slice(0,r).map(u=>({id:u.id,name:u.name,symbol:u.symbol,market_cap_rank:u.market_cap_rank}));return v.set(a,c,k.PRICE),c}async function ne(t,r,o){let a=f(t),n=o.toLowerCase(),e=`hist:${a}:${r}:${n}`,i=j.get(e);if(i)return i;let u=await(await E(`${N}/coins/${a}/history?date=${encodeURIComponent(r)}&localization=false`)).json();if(u.error||!u.market_data?.current_price?.[n])throw new w(u.error??`Historical price not found for ${t} on ${r}`);let p={id:a,date:r,price:u.market_data.current_price[n],vsCurrency:n};return j.set(e,p,k.HISTORICAL),p}async function oe(t,r,o){let a=f(t),n=r.toLowerCase(),e=String(o),i=`chart:${a}:${n}:${e}`,c=j.get(i);if(c)return c;let u=new URLSearchParams({vs_currency:n,days:e}),l=((await(await E(`${N}/coins/${a}/market_chart?${u}`)).json()).prices??[]).map(([C,g])=>({timestamp:C,price:g})),d={id:a,vs:n,days:e,prices:l};return j.set(i,d,k.HISTORICAL),d}async function ae(t,r){return(await P(t,r)).price}async function se(){let t="trending:coins",r=v.get(t);if(r)return r;let n=((await(await E(`${N}/search/trending`)).json()).coins??[]).map(({item:e})=>({id:e.id,name:e.name,symbol:e.symbol,market_cap_rank:e.market_cap_rank,score:e.score,price_btc:e.price_btc??null,price_usd:typeof e.data?.price=="number"?s(e.data.price,h("usd",e.data.price)):null,change_24h_usd:typeof e.data?.price_change_percentage_24h?.usd=="number"?y(e.data.price_change_percentage_24h.usd):null}));return v.set(t,n,k.TRENDING),n}async function ie(){let t="fear-greed:1",r=v.get(t);if(r)return r;let n=(await(await E("https://api.alternative.me/fng/?limit=1")).json()).data?.[0];if(!n)throw new w("Fear & Greed index unavailable");let e={value:Number(n.value),classification:n.value_classification,updatedAt:new Date(Number(n.timestamp)*1e3).toISOString()};return v.set(t,e,k.TRENDING),e}function te(t){return new Promise(r=>setTimeout(r,t))}function s(t,r=2){return Number.isFinite(t)?Number(t.toFixed(r)):0}function y(t,r=2){return Number.isFinite(t)?Number(t.toFixed(r)):0}function h(t,r){return t==="btc"||t==="eth"?8:r>=1?2:r>=.01?4:8}import{z as V}from"zod";async function q(){let t=O.get("usdt-pkr");if(t)return t;try{let o={rate:await De(),source:"binance_p2p",lastUpdated:new Date().toISOString()};return O.set("usdt-pkr",o,k.PKR),o}catch{let o={rate:await Oe(),source:"coingecko",lastUpdated:new Date().toISOString()};return O.set("usdt-pkr",o,k.PKR),o}}async function De(){let t=await fetch("https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":R},body:JSON.stringify({asset:"USDT",fiat:"PKR",merchantCheck:!1,page:1,payTypes:[],publisherType:null,rows:10,tradeType:"BUY"})});if(!t.ok)throw new Error(`Binance P2P API error: ${t.status}`);let o=(await t.json()).data?.slice(0,5)??[];if(o.length===0)throw new Error("No Binance P2P ads found for USDT/PKR");let a=o.map(e=>parseFloat(e.adv.price)).filter(e=>!isNaN(e));if(a.length===0)throw new Error("Invalid Binance P2P price data");let n=a.reduce((e,i)=>e+i,0)/a.length;return Number(n.toFixed(2))}async function Oe(){let r=(await S(["tether"],["pkr"])).tether?.pkr;if(r===void 0)throw new w("Could not fetch USDT/PKR rate from CoinGecko");return Number(r.toFixed(2))}async function M(t,r){let o=r.toLowerCase();if(o==="pkr")return{converted:t,rate:1,source:"direct"};if(o==="usdt"||o==="tether"){let u=await q();return{converted:Number((t*u.rate).toFixed(2)),rate:u.rate,source:u.source}}let a=f(o),e=(await S([a],["usd"]))[a]?.usd;if(e===void 0)throw new w(`Could not get USD price for ${r}`);let i=t*e,c=await q();return{converted:Number((i*c.rate).toFixed(2)),rate:Number((e*c.rate).toFixed(2)),source:`coingecko_usd + ${c.source}`}}async function ce(t,r){let o=r.toLowerCase();if(o==="pkr")return{converted:t,rate:1,source:"direct"};let a=await q(),n=t/a.rate;if(o==="usdt"||o==="tether"||o==="usd")return{converted:Number(n.toFixed(6)),rate:Number((1/a.rate).toFixed(8)),source:a.source};let e=f(o),c=(await S([e],["usd"]))[e]?.usd;if(!c)throw new w(`Could not get USD price for ${r}`);let u=n/c;return{converted:Number(u.toFixed(8)),rate:Number((a.rate/c).toFixed(2)),source:`coingecko_usd + ${a.source}`}}var ue=V.object({coin:V.string().describe("Coin id or symbol: btc, bitcoin, eth, sol, etc"),vs_currency:V.string().default("usd").describe("Fiat: usd, eur, pkr, inr, gbp, etc")});async function pe(t){let{coin:r,vs_currency:o}=t,a=o.toLowerCase();if(a==="pkr"){let n=await q(),e=await H(r,"usd"),i=(e.current_price??0)*n.rate;return{coin:e.id,symbol:e.symbol,name:e.name,price:s(i,2),vs_currency:"pkr","24h_change":e.price_change_percentage_24h?y(e.price_change_percentage_24h):null,market_cap:e.market_cap?s(e.market_cap*n.rate,0):null,market_cap_rank:e.market_cap_rank,total_volume:e.total_volume?s(e.total_volume*n.rate,0):null,high_24h:e.high_24h?s(e.high_24h*n.rate,2):null,low_24h:e.low_24h?s(e.low_24h*n.rate,2):null,ath:e.ath?s(e.ath*n.rate,2):null,ath_change_percent:e.ath_change_percentage?y(e.ath_change_percentage):null,usd_price:s(e.current_price,2),rate_source:n.source,last_updated:e.last_updated??new Date().toISOString()}}try{let n=await H(r,a),e=n.current_price??0;return{coin:n.id,symbol:n.symbol,name:n.name,price:s(e,h(a,e)),vs_currency:a,"24h_change":n.price_change_percentage_24h?y(n.price_change_percentage_24h):null,market_cap:n.market_cap?s(n.market_cap,0):null,market_cap_rank:n.market_cap_rank,total_volume:n.total_volume?s(n.total_volume,0):null,high_24h:n.high_24h?s(n.high_24h,h(a,n.high_24h)):null,low_24h:n.low_24h?s(n.low_24h,h(a,n.low_24h)):null,ath:n.ath?s(n.ath,h(a,n.ath)):null,ath_change_percent:n.ath_change_percentage?y(n.ath_change_percentage):null,atl:n.atl?s(n.atl,h(a,n.atl)):null,last_updated:n.last_updated??new Date().toISOString()}}catch{let n=await P(r,a);return{coin:n.id,price:s(n.price,h(a,n.price)),vs_currency:a,"24h_change":n.change24h?y(n.change24h):null,last_updated:n.lastUpdated??new Date().toISOString()}}}import{z}from"zod";var le=z.object({amount:z.number(),from:z.string(),to:z.string().describe("usd, pkr, btc, eth, etc")});async function me(t){let{amount:r,from:o,to:a}=t,n=o.toLowerCase(),e=a.toLowerCase();if(n===e)return{amount:r,from:n,to:e,converted:r,rate:1,source:"direct"};if(e==="pkr"){let g=await M(r,n);return{amount:r,from:f(n),to:"pkr",converted:g.converted,rate:g.rate,source:g.source}}if(n==="pkr"){let g=await ce(r,e);return{amount:r,from:"pkr",to:f(e),converted:g.converted,rate:g.rate,source:g.source}}let i=f(n),c=f(e),u=new Set(["usd","eur","gbp","jpy","inr","aud","cad","chf","cny"]);if(u.has(e)){let g=await P(n,e);return{amount:r,from:i,to:e,converted:s(r*g.price,2),rate:s(g.price,2),source:"coingecko"}}if(u.has(n)){let g=await P(e,n);return{amount:r,from:n,to:c,converted:s(r/g.price,8),rate:s(1/g.price,8),source:"coingecko"}}let p=await S([i,c],["usd"]),m=p[i]?.usd,l=p[c]?.usd;if(!m||!l)throw new Error(`Could not convert ${o} to ${a}`);let d=m/l,C=r*d;return{amount:r,from:i,to:c,converted:s(C,8),rate:s(d,8),source:"coingecko"}}import{z as de}from"zod";var Me=["eth","bnb","polygon","arbitrum","base","optimism"],he=de.object({chain:de.enum(Me).default("eth")});async function ge(t){let r=t.chain,o=`gas:${r}`,a=Y.get(o);if(a)return a;let n,e=ee[r],i=e?process.env[e.apiKeyEnv]:void 0;if(e&&i)try{n=await Le(r,e.url,i)}catch{}n||(n=await Ue(r));let c=await ze(n);return Y.set(o,c,k.GAS),c}function B(t){return t*K/1e9}async function ze(t){let r=Z[t.chain]??"ethereum",o=null;try{o=(await P(r,"usd")).price}catch{o=null}let a=B(t.low),n=B(t.average),e=B(t.high);return{...t,estimated_transfer:{gas_units:K,cost_native_low:s(a,8),cost_native_average:s(n,8),cost_native_high:s(e,8),cost_usd_low:o!==null?s(a*o,4):null,cost_usd_average:o!==null?s(n*o,4):null,cost_usd_high:o!==null?s(e*o,4):null,native_token:r}}}async function Le(t,r,o){let a=`${r}?module=gastracker&action=gasoracle&apikey=${o}`,n=await fetch(a,{headers:{"User-Agent":R,Accept:"application/json"}});if(!n.ok)throw new Error(`Scan API error: ${n.status}`);let e=await n.json();if(e.status!=="1"||!e.result)throw new Error("Invalid scan gas oracle response");return{chain:t,low:s(parseFloat(e.result.SafeGasPrice??"0"),2),average:s(parseFloat(e.result.ProposeGasPrice??"0"),2),high:s(parseFloat(e.result.FastGasPrice??"0"),2),baseFee:e.result.suggestBaseFee?s(parseFloat(e.result.suggestBaseFee),2):null,unit:"gwei",last_updated:new Date().toISOString(),source:`${t}scan`}}async function Ue(t){let r=X[t];if(!r)throw new Error(`Unsupported chain: ${t}`);try{let o=await fetch(`https://api.blocknative.com/gasprices/blockprices?chainid=${r}`,{headers:{Accept:"application/json","User-Agent":R}});if(o.ok){let n=(await o.json()).blockPrices?.[0],e=n?.estimatedPrices??[];if(e.length>0){let i=[...e].sort((l,d)=>l.price-d.price),c=i[0]?.price??0,u=i[i.length-1]?.price??0,p=i[Math.floor(i.length/2)]?.price??c,m=n?.baseFeePerGas?n.baseFeePerGas/1e9:null;return{chain:t,low:s(c,2),average:s(p,2),high:s(u,2),baseFee:m!==null?s(m,2):null,unit:"gwei",last_updated:new Date().toISOString(),source:"blocknative"}}}}catch{}return Ke(t)}function Ke(t){let r={eth:{low:15,average:25,high:40},bnb:{low:3,average:5,high:8},polygon:{low:30,average:50,high:80},arbitrum:{low:.1,average:.2,high:.5},base:{low:.01,average:.05,high:.1},optimism:{low:.01,average:.05,high:.1}},o=r[t]??r.eth;return{chain:t,low:o.low,average:o.average,high:o.high,baseFee:null,unit:"gwei",last_updated:new Date().toISOString(),source:"static_estimate"}}import{z as A}from"zod";var fe=A.object({buy_price:A.number(),sell_price:A.number(),quantity:A.number(),buy_fee_percent:A.number().default(0),sell_fee_percent:A.number().default(0),network_fee:A.number().default(0).describe("Fixed network/gas fee in the same unit as prices (e.g. USD)")});function _e(t){let r=t.buy_price,o=t.sell_price,a=t.quantity,n=t.buy_fee_percent??0,e=t.sell_fee_percent??0,i=t.network_fee??0,c=1+n/100,u=1-e/100,p=r*a*c,m=o*a*u,l=m-p,d=l-i,C=p+i,g=C>0?d/C*100:0,D=u>0?r*c/u+i/(a*u||1):r*c;return{buy_price:s(r,2),sell_price:s(o,2),quantity:s(a,8),buy_fee_percent:s(n,4),sell_fee_percent:s(e,4),network_fee:s(i,4),invested:s(p,2),returned:s(m,2),profit:s(d,2),profit_before_network_fee:s(l,2),roi_percent:y(g),break_even_price:s(D,2),is_profit:d>=0}}import{z as I}from"zod";var be=I.object({coin:I.string(),date:I.string().optional().describe("Single date DD-MM-YYYY. Use with investment snapshot."),days:I.union([I.number(),I.string()]).optional().describe("Chart range: 1, 7, 14, 30, 90, 180, 365, or max"),vs_currency:I.string().default("usd"),investment:I.number().default(1e3).describe("Hypothetical investment amount on the historical date")});function Ye(t){let r=t.match(/^(\d{2})-(\d{2})-(\d{4})$/);if(!r)throw new Error(`Invalid date format: ${t}. Expected DD-MM-YYYY (e.g. 15-01-2024)`);let[,o,a,n]=r,e=`${o}-${a}-${n}`,i=`${n}-${a}-${o}`,c=new Date(i);if(isNaN(c.getTime()))throw new Error(`Invalid date: ${t}`);let u=new Date;if(u.setHours(23,59,59,999),c>u)throw new Error(`Date cannot be in the future: ${t}`);return{formatted:e,iso:i}}function He(t){if(t.length===0)return null;let r=t.map(m=>m.price),o=r[0],a=r[r.length-1],n=Math.max(...r),e=Math.min(...r),i=a-o,c=o>0?i/o*100:0,u=Math.max(1,Math.floor(t.length/12)),p=t.filter((m,l)=>l%u===0||l===t.length-1).map(m=>({time:new Date(m.timestamp).toISOString(),price:s(m.price,h("usd",m.price))}));return{start_price:s(o,h("usd",o)),end_price:s(a,h("usd",a)),high:s(n,h("usd",n)),low:s(e,h("usd",e)),change:s(i,4),change_percent:y(c),points:t.length,sample:p}}async function ye(t){let r=(t.vs_currency??"usd").toLowerCase(),o=t.investment??1e3,a=f(t.coin);if(!t.date&&!t.days)throw new Error("Provide either date (DD-MM-YYYY) for a point-in-time price, or days (e.g. 7, 30, 90) for a range chart.");if(t.days!==void 0&&t.days!==null){let l=await oe(t.coin,r,t.days),d=He(l.prices);return{mode:"range",coin:a,vs_currency:r,days:String(t.days),summary:d}}let{formatted:n}=Ye(t.date),e=await ne(t.coin,n,r),i=await ae(t.coin,r),c=e.price>0?o/e.price:0,u=c*i,p=u-o,m=o>0?p/o*100:0;return{mode:"point",coin:a,date:n,vs_currency:r,historical_price:s(e.price,h(r,e.price)),current_price:s(i,h(r,i)),price_change:s(i-e.price,4),price_change_percent:y(e.price>0?(i-e.price)/e.price*100:0),investment_then:{invested:o,coins_bought:s(c,8),worth_now:s(u,2),gain_loss:s(p,2),gain_loss_percent:y(m)}}}import{z as W}from"zod";var we=W.object({query:W.string().describe("Name or symbol to search, e.g. pepe, solana, wif"),limit:W.number().int().min(1).max(50).default(10)});async function ke(t){let r=t.limit??10,o=await re(t.query,r);return{query:t.query,count:o.length,results:o}}import{z as L}from"zod";var ve=L.object({vs_currency:L.string().default("usd"),limit:L.number().int().min(1).max(50).default(10),order:L.enum(["market_cap_desc","market_cap_asc","volume_desc","volume_asc","id_asc","id_desc"]).default("market_cap_desc")});async function Ce(t){let r=(t.vs_currency??"usd").toLowerCase(),o=t.limit??10,a=t.order??"market_cap_desc",e=(await F(r,{perPage:o,order:a})).map(p=>({id:p.id,symbol:p.symbol,name:p.name,rank:p.market_cap_rank,price:s(p.current_price??0,h(r,p.current_price??0)),market_cap:p.market_cap?s(p.market_cap,0):null,total_volume:p.total_volume?s(p.total_volume,0):null,"24h_change":p.price_change_percentage_24h?y(p.price_change_percentage_24h):null})),i=e.filter(p=>p["24h_change"]!==null),c=[...i].sort((p,m)=>(m["24h_change"]??0)-(p["24h_change"]??0)).slice(0,3),u=[...i].sort((p,m)=>(p["24h_change"]??0)-(m["24h_change"]??0)).slice(0,3);return{vs_currency:r,order:a,count:e.length,coins:e,top_gainers:c,top_losers:u}}import{z as G}from"zod";var xe=G.object({coins:G.union([G.array(G.string()).min(2).max(5),G.string().describe("Comma-separated symbols, e.g. btc,eth,sol")]).describe("2\u20135 coins to compare"),vs_currency:G.string().default("usd")});function Ve(t){let r=Array.isArray(t)?t:t.split(/[,\s]+/).map(a=>a.trim()).filter(Boolean),o=[...new Set(r.map(a=>a.toLowerCase()))];if(o.length<2||o.length>5)throw new Error("Provide between 2 and 5 coins to compare");return o}async function Se(t){let r=(t.vs_currency??"usd").toLowerCase(),o=Ve(t.coins),a=o.map(f),n=await F(r,{ids:a,perPage:a.length}),e=new Map(n.map(m=>[m.id,m])),i=a.map((m,l)=>{let d=e.get(m);return d?{input:o[l],id:d.id,found:!0,symbol:d.symbol,name:d.name,rank:d.market_cap_rank,price:s(d.current_price??0,h(r,d.current_price??0)),market_cap:d.market_cap?s(d.market_cap,0):null,total_volume:d.total_volume?s(d.total_volume,0):null,"24h_change":d.price_change_percentage_24h?y(d.price_change_percentage_24h):null}:{input:o[l],id:m,found:!1}}),c=i.filter(m=>m.found),u=[...c].sort((m,l)=>(l["24h_change"]??-1/0)-(m["24h_change"]??-1/0))[0],p=[...c].sort((m,l)=>(l.market_cap??0)-(m.market_cap??0))[0];return{vs_currency:r,coins:i,highlights:{best_24h_performer:u?.id??null,largest_market_cap:p?.id??null}}}import{z as $}from"zod";var Be=$.object({coin:$.string(),amount:$.number().positive()}),Pe=$.object({holdings:$.array(Be).min(1).max(50).describe("List of { coin, amount } holdings"),vs_currency:$.string().default("usd")});async function Ie(t){let r=(t.vs_currency??"usd").toLowerCase(),o=t.holdings,a=o.map(l=>f(l.coin)),n=[...new Set(a)],e=r==="pkr"?"usd":r,i=await S(n,[e]),c=null,u=null;if(r==="pkr"){let l=await M(1,"usdt");c=l.rate,u=l.source}let p=o.map((l,d)=>{let C=a[d],g=i[C]?.[e];if(g===void 0)return{coin:C,amount:l.amount,found:!1,value:null};let D=l.amount*g,U=g;return r==="pkr"&&c!==null&&(D*=c,U*=c),{coin:C,amount:l.amount,found:!0,unit_price:s(U,h(r,U)),value:s(D,2)}}),m=p.reduce((l,d)=>l+(d.found&&d.value!==null?d.value:0),0);return{vs_currency:r,holdings:p,total_value:s(m,2),missing:p.filter(l=>!l.found).map(l=>l.coin),...u?{rate_source:u}:{}}}import{z as Re}from"zod";var Ee=Re.object({include_fear_greed:Re.boolean().default(!0)});async function Ae(t={include_fear_greed:!0}){let r=t.include_fear_greed??!0,[o,a]=await Promise.all([se(),r?ie():Promise.resolve(null)]);return{source:"coingecko",count:o.length,coins:o,...a?{fear_greed:{source:"alternative.me",value:a.value,classification:a.classification,updated_at:a.updatedAt}}:{}}}var Ze=[{name:"get_price",description:"Get live price of any cryptocurrency in any fiat, with market cap, volume, rank, ATH/ATL. Uses CoinGecko.",inputSchema:{type:"object",properties:{coin:{type:"string",description:"Coin id or symbol: btc, bitcoin, eth, sol, etc"},vs_currency:{type:"string",description:"Fiat: usd, eur, pkr, inr, gbp, etc",default:"usd"}},required:["coin"]}},{name:"convert",description:"Convert crypto amount to fiat/crypto. Optimized PKR via Binance P2P when requested.",inputSchema:{type:"object",properties:{amount:{type:"number"},from:{type:"string"},to:{type:"string",description:"usd, eur, pkr, btc, eth, etc"}},required:["amount","from","to"]}},{name:"gas_tracker",description:"Live gas fees for EVM chains (eth, bnb, polygon, arbitrum, base, optimism) with estimated transfer cost in native + USD.",inputSchema:{type:"object",properties:{chain:{type:"string",enum:["eth","bnb","polygon","arbitrum","base","optimism"],default:"eth"}}}},{name:"profit_calc",description:"Calculate crypto profit/loss, ROI, break-even. Supports exchange fees and fixed network/gas fee. No API needed.",inputSchema:{type:"object",properties:{buy_price:{type:"number"},sell_price:{type:"number"},quantity:{type:"number"},buy_fee_percent:{type:"number",default:0},sell_fee_percent:{type:"number",default:0},network_fee:{type:"number",default:0,description:"Fixed network fee in same unit as prices"}},required:["buy_price","sell_price","quantity"]}},{name:"historical_price",description:"Historical price on a date (DD-MM-YYYY) with investment snapshot, or a range chart via days (1/7/30/90/365/max).",inputSchema:{type:"object",properties:{coin:{type:"string"},date:{type:"string",description:"DD-MM-YYYY for point-in-time"},days:{type:["string","number"],description:"Range chart: 1, 7, 14, 30, 90, 180, 365, or max"},vs_currency:{type:"string",default:"usd"},investment:{type:"number",default:1e3,description:"Hypothetical investment on the historical date"}},required:["coin"]}},{name:"search_coin",description:"Search cryptocurrencies by name or symbol. Returns CoinGecko ids for use in other tools.",inputSchema:{type:"object",properties:{query:{type:"string"},limit:{type:"number",default:10}},required:["query"]}},{name:"top_coins",description:"Top cryptocurrencies by market cap or volume, with gainers/losers among the result set.",inputSchema:{type:"object",properties:{vs_currency:{type:"string",default:"usd"},limit:{type:"number",default:10},order:{type:"string",enum:["market_cap_desc","market_cap_asc","volume_desc","volume_asc"],default:"market_cap_desc"}}}},{name:"compare_coins",description:"Compare 2\u20135 cryptocurrencies side by side (price, mcap, volume, 24h change).",inputSchema:{type:"object",properties:{coins:{oneOf:[{type:"string"},{type:"array",items:{type:"string"}}],description:"Array or comma-separated list, e.g. btc,eth,sol"},vs_currency:{type:"string",default:"usd"}},required:["coins"]}},{name:"portfolio_value",description:"Value a portfolio of holdings in any fiat (batch pricing). Pass [{coin, amount}, ...].",inputSchema:{type:"object",properties:{holdings:{type:"array",items:{type:"object",properties:{coin:{type:"string"},amount:{type:"number"}},required:["coin","amount"]}},vs_currency:{type:"string",default:"usd"}},required:["holdings"]}},{name:"trending",description:"What's hot right now: CoinGecko trending coins plus Crypto Fear & Greed Index.",inputSchema:{type:"object",properties:{include_fear_greed:{type:"boolean",default:!0,description:"Include Alternative.me Fear & Greed Index (default true)"}}}}],et=[{name:"market-overview",description:"Global crypto market snapshot: top coins, gainers/losers, and trending + fear/greed",arguments:[{name:"vs_currency",description:"Fiat currency (default usd)",required:!1},{name:"limit",description:"How many top coins (default 10)",required:!1}]},{name:"whats-trending",description:"Trending coins and Fear & Greed Index snapshot",arguments:[]},{name:"analyze-coin",description:"Analyze a coin: live price, 7d history, and optional conversion",arguments:[{name:"coin",description:"Coin symbol or id",required:!0},{name:"vs_currency",description:"Fiat currency (default usd)",required:!1}]},{name:"trade-pnl",description:"Walk through a profit/loss calculation for a completed trade",arguments:[{name:"buy_price",description:"Entry price",required:!0},{name:"sell_price",description:"Exit price",required:!0},{name:"quantity",description:"Amount traded",required:!0},{name:"fees",description:"Optional fee notes (percent or network)",required:!1}]},{name:"gas-check",description:"Check current gas and estimated transfer cost for an EVM chain",arguments:[{name:"chain",description:"eth, bnb, polygon, arbitrum, base, or optimism",required:!1}]}];function J(t){return{content:[{type:"text",text:t}],isError:!0}}function x(t){return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}}function Te(t){t.setRequestHandler(Je,async()=>({tools:Ze})),t.setRequestHandler(Qe,async()=>({prompts:et})),t.setRequestHandler(Xe,async r=>{let{name:o,arguments:a}=r.params,n=a??{};switch(o){case"market-overview":{let e=n.vs_currency??"usd",i=n.limit??"10";return{description:"Market overview workflow",messages:[{role:"user",content:{type:"text",text:`Give me a crypto market overview in ${e}. 1) Call top_coins with limit ${i}. 2) Call trending. Summarize leaders, gainers/losers, what's trending, and Fear & Greed tone.`}}]}}case"whats-trending":return{description:"Trending + sentiment workflow",messages:[{role:"user",content:{type:"text",text:"Call the trending tool. List the hottest coins and explain the Fear & Greed reading in plain language."}}]};case"analyze-coin":{let e=n.coin;if(!e)throw new Error("coin is required");let i=n.vs_currency??"usd";return{description:`Analyze ${e}`,messages:[{role:"user",content:{type:"text",text:`Analyze ${e} in ${i}. 1) Call get_price. 2) Call historical_price with days=7. 3) Summarize price, market cap, 24h move, and 7-day trend.`}}]}}case"trade-pnl":{let{buy_price:e,sell_price:i,quantity:c,fees:u}=n;if(!e||!i||!c)throw new Error("buy_price, sell_price, and quantity are required");return{description:"Trade P&L workflow",messages:[{role:"user",content:{type:"text",text:`Calculate P&L with profit_calc: buy_price=${e}, sell_price=${i}, quantity=${c}.${u?` Fee notes: ${u}.`:""} Explain profit, ROI, and break-even clearly.`}}]}}case"gas-check":{let e=n.chain??"eth";return{description:`Gas check for ${e}`,messages:[{role:"user",content:{type:"text",text:`Check gas on ${e} with gas_tracker. Report low/average/high gwei and estimated simple-transfer cost in USD. Advise whether fees look cheap or expensive.`}}]}}default:throw new Error(`Unknown prompt: ${o}`)}}),t.setRequestHandler(We,async r=>{let{name:o,arguments:a}=r.params;try{switch(o){case"get_price":return x(await pe(ue.parse(a??{})));case"convert":return x(await me(le.parse(a??{})));case"gas_tracker":return x(await ge(he.parse(a??{})));case"profit_calc":return x(_e(fe.parse(a??{})));case"historical_price":return x(await ye(be.parse(a??{})));case"search_coin":return x(await ke(we.parse(a??{})));case"top_coins":return x(await Ce(ve.parse(a??{})));case"compare_coins":return x(await Se(xe.parse(a??{})));case"portfolio_value":return x(await Ie(Pe.parse(a??{})));case"trending":return x(await Ae(Ee.parse(a??{})));default:return J(`Unknown tool: ${o}`)}}catch(n){if(n instanceof w&&n.retryable)return J(`Rate limit hit: ${n.message}. Wait a few seconds and retry.`);let e=n instanceof Error?n.message:"Unknown error occurred";return J(e)}})}var Ne={name:"mcp-crypto-toolkit",version:"1.3.0",mcpName:"io.github.nad33mahm3d/mcp-crypto-toolkit",description:"Live crypto prices, conversion, gas tracker, portfolio tools and calculators for AI agents - MCP Server",type:"module",bin:{"mcp-crypto-toolkit":"dist/index.js"},files:["dist","README.md","LICENSE","CHANGELOG.md"],main:"./dist/index.js",exports:{".":"./dist/index.js"},scripts:{build:"tsup",dev:"tsup --watch",start:"node dist/index.js","start:stdio":"node dist/index.js","start:http":"node dist/http.js","dev:cf":"wrangler dev","deploy:cf":"wrangler deploy",test:"vitest run","test:watch":"vitest",inspector:"npx @modelcontextprotocol/inspector dist/index.js",prepack:"npm run build","pack:check":"npm pack --dry-run"},keywords:["mcp","mcp-server","crypto","bitcoin","ethereum","coingecko","gas","portfolio","claude","cursor","ai-agent"],license:"MIT",author:"Nadeem Ahmed <ebox.nadeem@gmail.com>",homepage:"https://github.com/nad33mahm3d/mcp-crypto-toolkit#readme",bugs:{url:"https://github.com/nad33mahm3d/mcp-crypto-toolkit/issues"},repository:{type:"git",url:"https://github.com/nad33mahm3d/mcp-crypto-toolkit.git"},publishConfig:{access:"public"},engines:{node:">=20"},dependencies:{"@modelcontextprotocol/sdk":"^1.0.0",zod:"^4.5.4"},devDependencies:{"@types/node":"^26.4.0",tsup:"^8.0.0",typescript:"^7.0.2",vitest:"^4.1.11",wrangler:"^4.129.0"}};var Ge=Ne.version,$e="mcp-crypto-toolkit",je="Live crypto prices, conversion, gas tracker, portfolio tools and calculators for AI agents";function Fe(){let t=new rt({name:$e,version:Ge,description:je},{capabilities:{tools:{},prompts:{}}});return Te(t),t}var ot=Fe(),at=new nt;await ot.connect(at);
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-crypto-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"mcpName": "io.github.nad33mahm3d/mcp-crypto-toolkit",
|
|
5
5
|
"description": "Live crypto prices, conversion, gas tracker, portfolio tools and calculators for AI agents - MCP Server",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"mcp-crypto-toolkit": "
|
|
8
|
+
"mcp-crypto-toolkit": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
"start": "node dist/index.js",
|
|
24
24
|
"start:stdio": "node dist/index.js",
|
|
25
25
|
"start:http": "node dist/http.js",
|
|
26
|
+
"dev:cf": "wrangler dev",
|
|
27
|
+
"deploy:cf": "wrangler deploy",
|
|
26
28
|
"test": "vitest run",
|
|
27
29
|
"test:watch": "vitest",
|
|
28
30
|
"inspector": "npx @modelcontextprotocol/inspector dist/index.js",
|
|
@@ -50,7 +52,7 @@
|
|
|
50
52
|
},
|
|
51
53
|
"repository": {
|
|
52
54
|
"type": "git",
|
|
53
|
-
"url": "
|
|
55
|
+
"url": "https://github.com/nad33mahm3d/mcp-crypto-toolkit.git"
|
|
54
56
|
},
|
|
55
57
|
"publishConfig": {
|
|
56
58
|
"access": "public"
|
|
@@ -66,6 +68,7 @@
|
|
|
66
68
|
"@types/node": "^26.4.0",
|
|
67
69
|
"tsup": "^8.0.0",
|
|
68
70
|
"typescript": "^7.0.2",
|
|
69
|
-
"vitest": "^4.1.11"
|
|
71
|
+
"vitest": "^4.1.11",
|
|
72
|
+
"wrangler": "^4.129.0"
|
|
70
73
|
}
|
|
71
74
|
}
|