Crypto Portfolio MCP
Tracks multi-exchange cryptocurrency portfolio P\&L by storing holdings and providing live price data.
README
Crypto Portfolio MCP
Track, analyze, and optimize your crypto portfolio directly inside Claude — multi-exchange, multi-coin, multi-wallet, with live prices and unrealized P&L.
MCP server (Model Context Protocol) that turns Claude into your personal crypto portfolio analyst. Connect it once and ask natural questions like "What's my BTC up by since I bought?" or "How would my net worth have looked during the May crash?" — Claude calls these tools under the hood.
What it does
A Claude-native portfolio tracker that:
- Records holdings across exchanges and wallets (Coinbase, Binance, Kraken, MetaMask, Ledger, anything)
- Calculates real P&L — current value, cost basis, unrealized gain/loss in USD and %, allocation breakdown
- Streams live prices from CoinGecko (Demo tier, free)
- Reconstructs historical value — what was your portfolio worth on March 14, 2024? Now you can ask
- Surveys the market — BTC dominance, trending coins, top-10 movers — without leaving the chat
It's the data layer for the companion Crypto Tax MCP, but works fully standalone.
Who it's for
| Persona | Why they use it |
|---|---|
| Multi-exchange holders | One place to ask "what am I actually worth?" instead of opening five apps |
| DeFi/Layer-1 explorers | Track holdings the exchange dashboards don't see (cold storage, hardware wallets) |
| Active traders | Quick P&L checks during a session without breaking flow |
| Tax prep | Feed the data into Crypto Tax MCP at year-end for 1099-DA reconciliation |
| Claude power users | You already chat with Claude all day — your portfolio should live there too |
Tools (7)
| Tool | Purpose |
|---|---|
get_portfolio_summary |
Total value + per-asset P&L + allocation % |
get_asset_price |
Live price for 1–50 coins |
add_holding |
Record a purchase (coin, qty, price, date, exchange) |
remove_holding |
Remove a holding (all entries or one exchange) |
get_portfolio_history |
Daily portfolio value over 1–365 days |
search_coin |
Resolve name/symbol → CoinGecko ID |
get_market_overview |
Market cap, BTC dominance, trending, top-10 |
Example conversations
These are the real prompts users say to Claude — what happens behind the scenes is shown in italics.
1. First-time setup: record a holding
You: "I bought 0.5 BTC on Coinbase on Jan 15, 2024 at $42,000."
Claude calls: search_coin("BTC") → finds bitcoin → add_holding({coin_id: "bitcoin", symbol: "BTC", quantity: 0.5, purchase_price_usd: 42000, purchase_date: "2024-01-15", exchange: "Coinbase"})
Claude: "Done. Tracking 0.5 BTC purchased Jan 15, 2024 at $42,000 on Coinbase ($21,000 cost basis)."
2. Check what you're worth right now
You: "What's my portfolio worth?"
Claude calls: get_portfolio_summary({currency: "usd"})
Claude: "Your portfolio is worth $73,420 across 5 assets, up +$18,920 (+34.7%) vs cost basis.
- BTC $48,200 (65.6%) — +$27,200 / +129% ↑
- ETH $15,100 (20.6%) — −$2,100 / −12% ↓
- SOL $6,800 (9.3%) — +$3,800 / +127% ↑
- USDT $2,500 (3.4%) — flat
- MATIC $820 (1.1%) — −$180 / −18% ↓"
<details> <summary>Raw JSON output</summary>
{
"total_value": 73420.00,
"total_cost_basis": 54500.00,
"total_unrealized_pnl_usd": 18920.00,
"total_unrealized_pnl_pct": 34.72,
"currency": "usd",
"asset_count": 5,
"holdings": [
{
"coin_id": "bitcoin",
"symbol": "BTC",
"quantity": 0.5,
"current_price": 96400,
"value": 48200,
"exchange": "Coinbase",
"purchase_price_usd": 42000,
"cost_basis": 21000,
"unrealized_pnl_usd": 27200,
"unrealized_pnl_pct": 129.52,
"price_change_24h_pct": 2.4,
"allocation_pct": 65.65
}
]
}
</details>
3. Quick price check (no holding needed)
You: "What's the price of SOL and AVAX right now?"
Claude calls: get_asset_price({coins: ["solana", "avalanche-2"]})
Claude: "SOL is at $185.40 (+3.2% 24h, $84B mcap). AVAX is at $42.10 (−1.8% 24h, $17B mcap)."
4. Find the right coin ID
You: "I want to add my Polygon holdings. What's the CoinGecko ID?"
Claude calls: search_coin({query: "polygon"})
Claude: "Polygon is now branded as POL with ID
polygon-ecosystem-token. The old MATIC token ismatic-network. Which one do you hold?"
5. Add a wallet holding (no price/date)
You: "Add 12 ETH in my Ledger cold storage. I don't remember the buy price."
Claude calls: add_holding({coin_id: "ethereum", symbol: "ETH", quantity: 12, exchange: "Ledger"})
Claude: "Added 12 ETH on Ledger. No cost basis recorded — current value will show, but unrealized P&L will be null. Add a purchase price later with
add_holdingif you want gain tracking."
6. Visualize the portfolio over time
You: "Show me how my portfolio performed over the last 90 days."
Claude calls: get_portfolio_history({days: 90})
Claude: "Your portfolio over 90 days:
- Day 1 (Mar 10): $58,200
- Peak (Apr 22): $81,400
- Trough (May 15): $52,100
- Today (Jun 8): $73,420
Net change: +$15,220 (+26.2%). You weathered the May correction and have recovered ~89% of the peak."
<details> <summary>Raw JSON output (truncated)</summary>
{
"currency": "usd",
"days": 90,
"data_points": 90,
"history": [
{"date": "2026-03-10", "value": 58200.00},
{"date": "2026-03-11", "value": 59100.00},
"...",
{"date": "2026-06-08", "value": 73420.00}
]
}
</details>
7. "What's everyone talking about?"
You: "Give me a market snapshot — what's hot right now?"
Claude calls: get_market_overview()
Claude: "Total crypto mcap: $3.2T (−1.2% 24h). BTC dominance: 56.8%. ETH: 14.2%.
Trending now: Hyperliquid (HYPE), Bittensor (TAO), Ondo (ONDO), Sui (SUI), Toncoin (TON).
Top 10: BTC $96.4K, ETH $3.4K, USDT (stable), BNB $710, SOL $185, USDC (stable), XRP $2.40, DOGE $0.42, ADA $1.10, AVAX $42."
8. Remove a holding
You: "I sold all my MATIC on Coinbase, remove it."
Claude calls: remove_holding({coin_id: "matic-network", exchange: "Coinbase"})
Claude: "Removed MATIC on Coinbase from your portfolio."
9. Multi-exchange same coin
You: "I have 0.3 BTC on Kraken and 0.2 BTC on Binance. Add both."
Claude calls add_holding twice with different exchange values.
Claude: "Tracking 0.5 BTC total across two exchanges (0.3 Kraken, 0.2 Binance).
get_portfolio_summarywill show them as separate rows so you can see exchange-level exposure."
10. Cost basis correction
You: "I made a typo earlier — my SOL buy price was $95, not $195. Fix it."
Claude calls: add_holding({coin_id: "solana", symbol: "SOL", quantity: <same>, purchase_price_usd: 95, ...}) — upsert merges by (user, coin, exchange).
Claude: "Updated. Your SOL cost basis is now $95/coin. Recalculated unrealized P&L: +$4,500 (was −$5,500 with the wrong price)."
Pricing tiers
| Tier | Holdings | History | Price |
|---|---|---|---|
| Free | 3 | 30 days | $0 |
| Pro | Unlimited | 365 days | $19/mo |
Free tier is fully functional for casual portfolios. Pro removes both limits — that's all.
Stack
- Runtime: Node.js 20, TypeScript
- MCP SDK:
@modelcontextprotocol/sdk(Streamable HTTP transport) - Framework: Express + Zod schemas
- Prices: CoinGecko Demo API — 30 req/min, 10K req/month, free. 60s response cache means a 10-user free portfolio uses ~80% fewer calls than naive fetching.
- Storage: SQLite via
better-sqlite3(single file, no external DB) - Auth: Bearer API key (SHA-256 hashed; raw key never persisted) + MCPize upstream proxy passthrough (
X-MCPize-Customer-Id+X-MCPize-Tier)
Development
# Clone + install
git clone https://github.com/biggling/crypto-portfolio-mcp.git
cd crypto-portfolio-mcp
npm install
# Configure
cp .env.example .env
# Edit .env — set COINGECKO_API_KEY from https://www.coingecko.com/en/api
# Run in dev (watch mode)
npm run dev
# Build + run production
npm run build
npm start
Environment variables
| Var | Purpose | Default |
|---|---|---|
PORT |
HTTP listen port | 3000 |
COINGECKO_API_KEY |
Demo-tier key from coingecko.com | required |
DB_PATH |
SQLite file path | ./data/holdings.db |
NODE_ENV |
production or development |
production |
MCPIZE_UPSTREAM_TOKEN |
Upstream proxy token for MCPize gateway | optional |
Create a test API key
node -e "
const db = require('better-sqlite3')('./data/holdings.db');
const crypto = require('crypto');
const key = 'pk_test_' + crypto.randomBytes(24).toString('hex');
const hash = crypto.createHash('sha256').update(key).digest('hex');
db.prepare('INSERT INTO api_keys (key_hash, tier) VALUES (?, ?)').run(hash, 'pro');
console.log('Test key:', key);
"
Test it works
curl -X POST http://localhost:3000/mcp \
-H "Authorization: Bearer YOUR_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
You should see all 7 tools listed in the response.
Deployment
For production deployment (VPS + nginx + Let's Encrypt + systemd), use:
sudo bash deploy.sh "https://github.com/biggling/crypto-portfolio-mcp.git" "mcp.example.com"
The script installs Node 20, builds, generates a systemd unit, and walks you through nginx + certbot. Verify with bash test-deployment.sh https://mcp.example.com YOUR_KEY.
Architecture
Claude AI (or MCPize gateway)
│ HTTPS Bearer <key>
▼
nginx reverse proxy ──► proxy_buffering off, 300s timeouts (Streamable HTTP needs this)
│ :3000
▼
crypto-portfolio-mcp (Node.js / Express)
├─► SQLite ── api_keys, holdings
└─► CoinGecko ── 60s in-memory cache
Auth flow
- Client sends
Authorization: Bearer <key>(direct buyer) orAuthorization: Bearer <upstream-token>+X-MCPize-Customer-Id+X-MCPize-Tier(via MCPize gateway). - Server identifies user via SHA-256 hash — the plaintext key is never stored.
- Tools receive
userId(the hash) andtier(free|pro) in context. - Tier limits enforced inside each tool: free tier caps holdings at 3 and history at 30 days.
Roadmap
- Phase 1 (now): Portfolio P&L tracker — ✅ shipped
- Phase 2: Crypto Tax MCP (FIFO/LIFO/HIFO lot matching, 1099-DA reconciliation) — ✅ shipped (companion repo)
- Phase 3: OAuth 2.1 PKCE auth for the Anthropic directory
- Phase 4: Polar.sh / Stripe billing for direct buyers
- Phase 5: Multi-chain wallet sync (Moralis, Etherscan, on-chain holdings auto-discovery)
License
MIT — do whatever, but no warranty.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。