mcp-crypto-toolkit 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.1.0] - 2026-09-03
11
+
12
+ ### Added
13
+
14
+ - Tools: `search_coin`, `top_coins`, `compare_coins`, `portfolio_value`
15
+ - Richer `get_price` (market cap, volume, rank, ATH/ATL)
16
+ - `historical_price` range mode via `days`
17
+ - Gas USD transfer estimates + Optimism chain
18
+ - `profit_calc` network fee support
19
+ - Optional `COINGECKO_API_KEY`
20
+ - MCP prompts: `market-overview`, `analyze-coin`, `trade-pnl`, `gas-check`
21
+ - HTTP transport (`npm run start:http`)
22
+ - Vitest unit tests and GitHub Actions CI
23
+ - `demo.gif`
24
+
25
+ ### Changed
26
+
27
+ - Package renamed to `mcp-crypto-toolkit` (npm name conflict avoidance)
28
+
29
+ ## [1.0.0] - 2026-09-02
30
+
31
+ ### Added
32
+
33
+ - Initial release: `get_price`, `convert`, `gas_tracker`, `profit_calc`, `historical_price`
34
+ - CoinGecko client with cache + retry
35
+ - Binance P2P PKR path for conversions
36
+ - MIT license and README
37
+
38
+ [Unreleased]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.1.0...HEAD
39
+ [1.1.0]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/compare/v1.0.0...v1.1.0
40
+ [1.0.0]: https://github.com/nad33mahm3d/mcp-crypto-toolkit/releases/tag/v1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mcp-crypto-price contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # mcp-crypto-toolkit
2
+
3
+ [![npm version](https://img.shields.io/npm/v/mcp-crypto-toolkit.svg)](https://www.npmjs.com/package/mcp-crypto-toolkit)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![MCP](https://img.shields.io/badge/MCP-Server-blue.svg)](https://modelcontextprotocol.io)
6
+ [![CI](https://github.com/nad33mahm3d/mcp-crypto-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/nad33mahm3d/mcp-crypto-toolkit/actions/workflows/ci.yml)
7
+
8
+ **Live crypto prices, conversion, gas tracker, portfolio tools, and calculators for AI agents.**
9
+
10
+ Ask Claude: *What's ETH doing? Top 10 coins? Gas on Base? What's my portfolio worth?*
11
+
12
+ ![Demo](./demo.gif)
13
+
14
+ ## Features
15
+
16
+ - Live prices with market cap, volume, rank, ATH/ATL (CoinGecko)
17
+ - Convert between crypto and fiat (optimized PKR via Binance P2P when needed)
18
+ - Search coins by name/symbol
19
+ - Top coins + gainers/losers snapshot
20
+ - Compare 2–5 coins side by side
21
+ - Portfolio valuation (batch holdings)
22
+ - Historical point-in-time + range charts (7d/30d/…)
23
+ - EVM gas tracker with USD transfer estimates
24
+ - Profit/loss calculator with exchange + network fees
25
+ - MCP prompts for common workflows
26
+ - **Zero config** — no API key required (optional `COINGECKO_API_KEY` for higher limits)
27
+ - stdio + HTTP transports
28
+
29
+ ## Quick Install
30
+
31
+ ```bash
32
+ npx -y mcp-crypto-toolkit
33
+ ```
34
+
35
+ ## Installation
36
+
37
+ ### Claude Desktop / Cursor / Windsurf
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "crypto-toolkit": {
43
+ "command": "npx",
44
+ "args": ["-y", "mcp-crypto-toolkit"]
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ Local build:
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "crypto-toolkit": {
56
+ "command": "node",
57
+ "args": ["/ABSOLUTE/PATH/TO/mcp-crypto-toolkit/dist/index.js"]
58
+ }
59
+ }
60
+ }
61
+ ```
62
+
63
+ ### HTTP server
64
+
65
+ ```bash
66
+ npm run build
67
+ npm run start:http
68
+ # POST http://localhost:3000/mcp
69
+ ```
70
+
71
+ ## Tools (9)
72
+
73
+ | Tool | Description |
74
+ |------|-------------|
75
+ | `get_price` | Live price + market cap, volume, rank, ATH/ATL |
76
+ | `convert` | Crypto ↔ fiat/crypto conversion |
77
+ | `search_coin` | Search by name or symbol |
78
+ | `top_coins` | Top N by market cap/volume + gainers/losers |
79
+ | `compare_coins` | Side-by-side compare 2–5 coins |
80
+ | `portfolio_value` | Value a list of holdings |
81
+ | `historical_price` | Point date or range chart + investment snapshot |
82
+ | `gas_tracker` | Gas fees + estimated transfer cost (USD) |
83
+ | `profit_calc` | P&L, ROI, break-even (fees + network fee) |
84
+
85
+ ## Prompts
86
+
87
+ | Prompt | Purpose |
88
+ |--------|---------|
89
+ | `market-overview` | Top coins + market tone |
90
+ | `analyze-coin` | Price + 7d trend for one coin |
91
+ | `trade-pnl` | Walk through a P&L calc |
92
+ | `gas-check` | Gas + transfer cost advice |
93
+
94
+ ## Example prompts
95
+
96
+ 1. *What's the current price of Bitcoin and its market cap?*
97
+ 2. *Search for coins matching "pepe"*
98
+ 3. *Show top 10 coins and today's gainers*
99
+ 4. *Compare BTC, ETH, and SOL*
100
+ 5. *Value my portfolio: 0.5 BTC and 2 ETH in USD*
101
+ 6. *What was ETH on 01-01-2024, and what would $1000 then be worth now?*
102
+ 7. *Show SOL's 30-day price range*
103
+ 8. *What's gas on Base right now in USD?*
104
+ 9. *I bought at 100, sold at 120, qty 10, 0.1% fees — profit?*
105
+
106
+ ## Development
107
+
108
+ ```bash
109
+ git clone https://github.com/nad33mahm3d/mcp-crypto-toolkit
110
+ cd mcp-crypto-toolkit
111
+ npm install
112
+ npm test
113
+ npm run build
114
+ npm run inspector
115
+ ```
116
+
117
+ ### Optional env
118
+
119
+ | Variable | Purpose |
120
+ |----------|---------|
121
+ | `COINGECKO_API_KEY` | Higher CoinGecko rate limits (still optional) |
122
+ | `COINGECKO_PRO=1` | Use Pro header with your key |
123
+ | `ETHERSCAN_API_KEY` / `BSCSCAN_API_KEY` / … | Better gas oracles |
124
+ | `OPTIMISM_API_KEY` | Optimism gas oracle |
125
+ | `PORT` | HTTP server port (default 3000) |
126
+
127
+ Without gas API keys, gas falls back to Blocknative then static estimates.
128
+
129
+ ## Contributing
130
+
131
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup, PR guidelines, and the release process.
132
+
133
+ - [Code of Conduct](./CODE_OF_CONDUCT.md)
134
+ - [Security Policy](./SECURITY.md)
135
+ - [Support](./SUPPORT.md)
136
+ - [Changelog](./CHANGELOG.md)
137
+
138
+ ### Releasing (maintainers)
139
+
140
+ 1. Add `NPM_TOKEN` repo secret (npm Automation token)
141
+ 2. Update `CHANGELOG.md`
142
+ 3. Create a GitHub Release with tag `vX.Y.Z`
143
+ 4. Actions publishes to npm with provenance automatically
144
+
145
+ ## API Credits
146
+
147
+ - [CoinGecko](https://www.coingecko.com/) — prices & markets (free tier, no key required)
148
+ - [Binance P2P](https://p2p.binance.com/) — USDT/PKR when converting to PKR
149
+ - [Etherscan](https://etherscan.io/) family — optional gas oracles
150
+ - [Blocknative](https://www.blocknative.com/) — gas fallback
151
+
152
+ ## License
153
+
154
+ MIT — see [LICENSE](./LICENSE)
package/dist/http.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/http.js ADDED
@@ -0,0 +1,2 @@
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`)});
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
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);
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "mcp-crypto-toolkit",
3
+ "version": "1.1.0",
4
+ "description": "Live crypto prices, conversion, gas tracker, portfolio tools and calculators for AI agents - MCP Server",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcp-crypto-toolkit": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE",
13
+ "CHANGELOG.md"
14
+ ],
15
+ "main": "./dist/index.js",
16
+ "exports": {
17
+ ".": "./dist/index.js"
18
+ },
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "start": "node dist/index.js",
23
+ "start:stdio": "node dist/index.js",
24
+ "start:http": "node dist/http.js",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "inspector": "npx @modelcontextprotocol/inspector dist/index.js",
28
+ "prepack": "npm run build",
29
+ "pack:check": "npm pack --dry-run"
30
+ },
31
+ "keywords": [
32
+ "mcp",
33
+ "mcp-server",
34
+ "crypto",
35
+ "bitcoin",
36
+ "ethereum",
37
+ "coingecko",
38
+ "gas",
39
+ "portfolio",
40
+ "claude",
41
+ "cursor",
42
+ "ai-agent"
43
+ ],
44
+ "license": "MIT",
45
+ "author": "Nadeem Ahmed <ebox.nadeem@gmail.com>",
46
+ "homepage": "https://github.com/nad33mahm3d/mcp-crypto-toolkit#readme",
47
+ "bugs": {
48
+ "url": "https://github.com/nad33mahm3d/mcp-crypto-toolkit/issues"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/nad33mahm3d/mcp-crypto-toolkit.git"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "engines": {
58
+ "node": ">=18"
59
+ },
60
+ "dependencies": {
61
+ "@modelcontextprotocol/sdk": "^1.0.0",
62
+ "zod": "^3.23.0"
63
+ },
64
+ "devDependencies": {
65
+ "@types/node": "^20.0.0",
66
+ "tsup": "^8.0.0",
67
+ "typescript": "^5.4.0",
68
+ "vitest": "^3.0.0"
69
+ }
70
+ }