Skip to main content
Glama
tamasPetki

HeadlessTracker

by tamasPetki

headless-tracker

npm version

🤖 This project is being developed and maintained autonomously by Hex, an AI dev agent. Decisions log: decisions.md · Daily build log: daily-log.md · Hex on Bluesky · Solo team. No human in the dev loop.

⚠️ Not financial advice. HeadlessTracker is a portfolio data aggregation tool. For informational purposes only. See DISCLAIMER.md for full text.

A read-only MCP server that lets your AI host (Claude Desktop, Claude Code, Cursor, ChatGPT) see your whole crypto portfolio across exchanges, on-chain wallets, and prediction markets — without ever giving it your API keys, and with no ability to trade or move funds. It reads the numbers; it can't touch the money.

The thesis: AI hosts (Claude Desktop, Claude Code, Cursor, ChatGPT) generate dashboards on demand from structured data. Building yet another tracker UI is wasted work in 2026 — there is no single UI to build, your AI renders whatever view you ask for. Build the data layer; let the AI host be the renderer.

Status: Production-ready and live on npm (version badge above). Six connectors (Bybit, Binance, MetaMask/EVM, Solana, Hyperliquid, Polymarket), 15 MCP tools, an interactive multi-tab dashboard panel, a CLI for terminal queries, and a 428-test suite. Runs under plain Node (npx headless-tracker) or Bun, working end-to-end with Claude Desktop.

  • 6 connectors: Bybit, Binance Spot+Futures, MetaMask multi-chain + multi-wallet, Solana multi-wallet, Hyperliquid (perp + spot, address-only), Polymarket

  • 15 MCP tools: 6 data + 7 account/token management + 2 MCP App panels

  • 3 MCP prompts (views): portfolio-dashboard, weekly-review, risk-check — see TEMPLATES.md, and contribute a view

  • Interactive dashboard MCP App: 3 tabs (Portfolio / Weekly / Risk) with donut + bar charts, currency switcher, refresh button

  • Live Settings MCP App for setup and admin

  • CLI portfolio queries: show holdings / pnl / transactions (no Claude required)

  • Custom ERC-20 token lists; FIFO + Average Cost on transaction history

  • Multi-currency display (USD/EUR/GBP/HUF); CoinGecko + Jupiter spot and historical prices

  • Time-windowed PnL (--timeframe=24h|7d|30d|ytd)

  • 428-test suite; runs under plain Node or Bun

  • Read-only & local-first: no orders/withdrawals/transfers; 4 of 6 connectors need only a public address; secrets live in your OS keychain and never enter the model's context — see SECURITY.md

See ROADMAP.md for what's done, what's next, and what's intentionally out of scope.

What it does

Connects to your accounts (read-only), normalizes everything into a single schema, exposes it as MCP tools. Then you ask Claude (or any MCP host):

  • "What do I own?"

  • "How is my portfolio split between crypto and prediction markets?"

  • "Show my Polymarket positions grouped by event."

  • "Refresh Bybit and tell me my BTC P&L."

The AI host generates the chart, the table, the breakdown. You don't build a UI.

Related MCP server: zerion-mcp

Try it in 60 seconds (no API keys)

The zero-setup version — one command, no accounts, no keys, not even an address. See a full sample portfolio (five venues; crypto + cash + prediction markets) rendered exactly as your AI host receives it:

npx headless-tracker demo
account                 symbol               class       qty       value    price
──────────────────────  ───────────────────  ──────────  ────────  ───────  ───────
bybit:UNIFIED           BTC                  crypto      0.420000  $25704   $61200
binance:spot            SOL                  crypto      95.0000   $14440   $152.00
metamask:0xd8d2…f1a3    WBTC                 crypto      0.150000  $9150    $61000
solana:7vfC…Wd9k        JUP                  crypto      1800.00   $1656    $0.9200
polymarket:0x9c1a…7b20  RATE-CUT-2026 (YES)  prediction  1500.00   $930.00  $0.6200
…
Total: $104126  (15 positions across 5 venues)

Allocation by asset class:
  crypto         $88024   84.5%  ████████████████████
  cash           $14900   14.3%  ███
  prediction      $1202    1.2%  █

It also prints the plain-English questions you'd ask Claude ("what do I own across everything?", "how is it split?") mapped to the MCP tool that answers each. When you want your own numbers, it's the same loop with a real address or read-only key:

You shouldn't have to hand a new tool your exchange keys just to find out whether it's any good. Solana, Hyperliquid, and Polymarket read public on-chain addresses, so you can point HeadlessTracker at any wallet you can see (your own included) with zero credentials. (Hyperliquid is fully keyless — perp positions, account equity, and spot balances all read from just the address you trade from.)

# install (or prefix any command with `npx`)
npm install -g headless-tracker

# add a public Solana wallet: no API key, just the address
headless-tracker setup solana
#   Solana address (base58): <paste any public address>
#   (press ENTER through the optional RPC + dust prompts)

# print the holdings right in your terminal, no Claude required
headless-tracker show holdings
account            symbol  class   qty       value    price
─────────────────  ──────  ──────  ────────  ───────  ────────
solana:7Xk2…q9Fa   SOL     crypto  12.4081   $2604    $209.88
solana:7Xk2…q9Fa   USDC    crypto  540.0000  $540.00  $1.00
solana:7Xk2…q9Fa   JUP     crypto  1200.00   $612.00  $0.5100

Total: $3756  (3 positions across 1 accounts)

(Example output; the account id is shortened here for width. Your numbers come from the live chain.)

That is the whole loop: install, point at a public address, see normalized holdings. When you want your private accounts (Bybit, Binance), setup those too. Every connector uses read-only credentials, kept in your OS keychain, never written to disk and never sent anywhere except the exchange's own API. Then wire it into Claude and ask "what do I own?" to get the same data as a chat-native dashboard.

Non-interactive setup (scripts, Docker, CI)

setup also runs without prompts — pass flags, and keep any secret in an environment variable (never on the command line, so it stays out of your shell history):

# public-address connectors: everything via flags, zero secrets
headless-tracker setup solana --address=<base58> --dust=0.5
headless-tracker setup hyperliquid --address=0x...   # perp + spot, no key
headless-tracker setup polymarket --proxy-wallet=0x... 

# connectors with a secret: non-secret config via flags, secret via env
HT_SETUP_ETHERSCAN_KEY=… headless-tracker setup metamask --address=0x... --chains=1,137
HT_SETUP_API_KEY=… HT_SETUP_API_SECRET=… headless-tracker setup bybit --account-type=UNIFIED --also=FUND

Headless / no OS keychain (Docker, WSL, many Linux servers, CI): there's no Secret Service to write to, so setup registers the account and prints the exact HEADLESS_TRACKER_<CONNECTOR>_<ACCOUNT> env var to set with a JSON credential object — e.g. HEADLESS_TRACKER_SOLANA_<ADDR>='{"address":"…","dustThresholdUsd":0.5}'. Set it in your MCP server's environment and the data tools read credentials from there. Nothing is ever written to disk.

Interactive dashboard (live UI panel)

For hosts that support MCP Apps — Claude Desktop, ChatGPT, Goose, VS Code — say:

Show my dashboard

The host renders a sandboxed iframe in the chat panel with three live tabs:

  • Portfolio — total value KPIs, top positions table, allocation-by-symbol donut (top 7 + "Other" tail), warnings + failures

  • Weekly — 7-day window delta KPIs, recent trades table, skipped-symbols disclosure (with reasons)

  • Risk — concentration audit (single-position, venue, stablecoin reserve, prediction-market overweight) scored PASS / WARN / ALERT, by-venue donut

Plus a currency switcher (USD / EUR / GBP / HUF) and a refresh button. The iframe makes its own follow-up tool calls as the user clicks tabs — no extra prompting needed once it's open. Optional args:

Open the dashboard in HUF, weekly tab

Implementation: src/mcp/apps/dashboard/ (browser-side TS bundled into a single dist/mcp-apps/dashboard.html via bun run build:apps, ships with the package). The bundled artifact ships inside the npm package so users running npx headless-tracker don't need a build step.

If your host doesn't render MCP Apps yet, the render_dashboard tool still returns a textual confirmation. Use the prompt cookbook below as a fallback — same workflows, same data, just no live UI panel.

Settings panel (live UI for setup + admin)

For setup that doesn't drop you into a terminal, ask:

Open settings

The Settings MCP App opens with four tabs:

  • Accounts — list of configured accounts with a Remove button (one-way confirm dialog; deletes from both the OS keychain and the registry).

  • Add Account — forms for Bybit / Binance / MetaMask / Solana / Hyperliquid / Polymarket. Each form validates against the upstream API before persisting credentials. Explicit security disclosure at the top: credentials submitted via the form transit Claude Desktop's process en route to the keychain. All six connectors use READ-ONLY credentials by design (Bybit "Read" only, no Withdraw; Binance "Enable Reading" only, no Trade or Withdraw; Etherscan is a public-data rate-limit token; Polymarket proxy wallet is already public; Solana and Hyperliquid addresses are public on-chain identifiers — Hyperliquid needs no key or signature at all). Worst-case leak = portfolio-read, never fund movement. For zero-trust, the CLI flow (bun run setup <connector>) stays available.

  • Wallets — add an additional wallet address to an existing MetaMask OR Solana account (multi-wallet under one MCP account, sharing the same Etherscan key/chain selection or RPC URL).

  • Custom Tokens — list / add / remove ERC-20 tokens per chain. Token data is public on-chain; no keychain involvement.

Either path (CLI or Settings UI) writes to the same ~/.headless-tracker/cache.db + OS keychain, so accounts created via either show up immediately in the dashboard and CLI.

Quick start

1. Install

No clone, no build step, no Bun required. The package runs under plain Node (≥ 22.5) or Bun. Install it globally:

npm install -g headless-tracker

Or run any command without installing by prefixing npx, e.g. npx headless-tracker setup solana. (Building from source for development uses Bun — see Development.)

2. Configure your accounts (interactive)

Run setup for each integration you want. Each prompts for credentials, validates them, and stores them in your OS keychain (macOS Keychain, Linux Secret Service, Windows Credential Vault). On a headless box with no keychain, see Headless / no OS keychain below.

headless-tracker setup bybit
headless-tracker setup binance
headless-tracker setup metamask
headless-tracker setup solana
headless-tracker setup polymarket

Verify what's configured:

headless-tracker list-accounts

Headless / no OS keychain (Docker, WSL, servers, CI)

The OS keychain needs a running secret service (Secret Service / D-Bus on Linux, Keychain on macOS, Credential Vault on Windows). Plenty of real environments don't have one: a Docker container, WSL, a bare Linux server, a CI job. There, the keychain write fails.

In that case setup does not abort. It still registers the account, then prints the exact environment variable to set, for example:

⚠  OS keychain unavailable, so credentials were NOT stored (...). The account is
   registered. ... set the HEADLESS_TRACKER_SOLANA_<ADDR> environment variable to a
   JSON object in your MCP server's env, then restart.

Set that variable to the connector's credential JSON in your MCP server's env block (or your shell), then restart. The env var always takes precedence over the keychain, so this also works as an explicit override. Per-connector JSON shapes (use read-only API keys — see the security note in the Settings panel section):

Connector

Env var (printed by setup)

JSON value

Bybit

HEADLESS_TRACKER_BYBIT_<ACCOUNTTYPE>

{"apiKey":"...","apiSecret":"...","accountType":"UNIFIED"}

Binance

HEADLESS_TRACKER_BINANCE_KEY_<FIRST6>

{"apiKey":"...","apiSecret":"...","includeFutures":false}

MetaMask

HEADLESS_TRACKER_METAMASK_0X<ADDR>

{"address":"0x...","etherscanApiKey":"...","chainIds":[1],"trackCommonTokens":true,"hasEtherscanPro":false}

Solana

HEADLESS_TRACKER_SOLANA_<ADDR>

{"address":"<base58>"} (optional "rpcUrl", "dustThresholdUsd")

Hyperliquid

HEADLESS_TRACKER_HYPERLIQUID_0X<ADDR>

{"address":"0x..."} (optional "dustThresholdUsd")

Polymarket

HEADLESS_TRACKER_POLYMARKET_0X<ADDR>

{"proxyWallet":"0x...","sizeThreshold":0.01}

The variable name is derived from the account identifier (setup prints the exact string, so you don't have to construct it by hand). Example for a Claude Desktop / MCP config env block:

"env": {
  "HEADLESS_TRACKER_SOLANA_<ADDR>": "{\"address\":\"<base58 address>\"}"
}

Optional: request timeout

Each per-account fetch is bounded by a deadline (default 30s) so a hung upstream can never stall a tool call — it degrades to a network_timeout failure for that one account while the rest return. Override it with HEADLESS_TRACKER_REQUEST_TIMEOUT_MS (e.g. raise it if you track many EVM chains on a single MetaMask account and see spurious timeouts).

3. Wire up Claude Desktop

Edit your claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\):

{
  "mcpServers": {
    "headless-tracker": {
      "command": "npx",
      "args": ["-y", "headless-tracker"]
    }
  }
}

That's the whole config: no absolute paths, no clone location, no Bun. npx -y headless-tracker with no subcommand starts the MCP server over stdio (npx caches after the first run). If you installed globally with npm i -g headless-tracker, you can use "command": "headless-tracker" with no args instead.

Restart Claude Desktop (Cmd+Q, then reopen — the in-app "new conversation" doesn't reload config).

4. Try it

Open a new conversation in Claude Desktop:

What's in my portfolio?

How is my portfolio split between crypto and prediction markets?

Show my Polymarket positions sorted by current value.

If Claude doesn't see the tools, check ~/Library/Logs/Claude/mcp-server-headless-tracker.log.

5. Use the interactive dashboard (MCP App)

Once setup works, ask:

Show my dashboard

The live UI panel pops up in the chat. See the Interactive dashboard section above for what's in each tab and how to pass args.

6. Use the preset prompts (one-click dashboards)

The MCP server ships three prompt templates. They show up in Claude Desktop's prompt picker (the / or attachment menu, depending on version) and in Claude Code as slash commands. Each one steers Claude through a specific multi-tool workflow:

Prompt

What it does

portfolio-dashboard

Calls get_holdings + get_allocations + get_pnl + get_polymarket_positions in parallel and renders a complete multi-section dashboard (HTML artifact when supported).

weekly-review

7-day window delta + biggest movers + recent trades + one observation. Approximation caveat surfaced honestly (current basket at historical prices, NOT trades within the window).

risk-check

Concentration / venue / stablecoin reserve / prediction-market overweight / chain concentration. Each dimension scored PASS / WARN / ALERT with the actual percentages.

You can also paste any of these prompts directly — they're plain text. See the cookbook below.

Prompt cookbook

Copy-paste these into any MCP-aware client. Each one expects the headless-tracker MCP server to be configured. None of them require new code on the server side.

Quick "where do I stand"

Build me a complete portfolio dashboard. Call get_holdings, get_allocations (by asset_class and by symbol), get_pnl, and get_polymarket_positions in parallel and synthesize a single dashboard artifact. Show top 10 positions, asset-class breakdown, total PnL. Be honest about NULL fields — don't fabricate.

Quick "how was this week"

Give me a 7-day review. Call get_pnl with timeframe=7d, get_holdings, and get_transactions with since=7d. Surface windowDelta with the approximation caveat ("current basket at historical prices, not trades within the window"), list the trades by exchange, and end with one short observation about what drove the change.

Quick "should I be worried"

Risk check my portfolio. Call get_holdings and get_allocations (by symbol, by asset_class, by connector). Score each: single-position concentration (ALERT > 40%), venue concentration (ALERT > 70%), stablecoin reserve (WARN < 5%, ALERT = 0%), prediction-market overweight (WARN > 15%). Output as a markdown table.

Tax season

I need to do my taxes. Call get_transactions for the past year (since=365d). Then call get_pnl with include_history=true and method=fifo. Group realized PnL by symbol and by month. Flag any sales with unknown cost basis (deposits / transfers without price) — those are honest gaps I'll have to research separately.

HUF view (or EUR / GBP)

Show my portfolio in Hungarian forint. Call get_holdings with currency=HUF. Sort by value descending. Sum the total in HUF and tell me whether the FX source was the live API or the static fallback.

Polymarket bet review

Walk me through my Polymarket positions. Call get_polymarket_positions with group_by_event=true. Then call get_pnl with include_history=true to get realized PnL via FIFO over /trades. For each event, show: title, my outcome holdings, current value, realized PnL so far, and end date. Flag any redeemable positions I should claim.

Quick portfolio queries from the CLI (no Claude required)

For the 3-second "what's in my portfolio?" question without opening Claude Desktop:

headless-tracker show holdings
headless-tracker show pnl
headless-tracker show transactions --since=7d

Each prints a text table. Filters work: show holdings --account-id=bybit:UNIFIED, show holdings --asset-class=crypto, show transactions --since=24h --account-id=metamask:0xabc.

Multi-currency display

show holdings defaults to USD. Pass --currency= for live FX-converted values:

headless-tracker show holdings --currency=HUF
headless-tracker show holdings --currency=EUR

FX rates come from a free public API (exchangerate-api.com) with frankfurter.dev as fallback, plus a static fallback if both fail (which surfaces as a warning so you know the displayed numbers may be a few percent stale). Supported: USD, EUR, GBP, HUF.

Cost basis methods (FIFO vs Average)

For honest realized P&L based on your transaction history (not connector metadata which can mix realized + unrealized for prediction markets):

headless-tracker show pnl --include-history=true
headless-tracker show pnl --include-history=true --method=average

--method=fifo (default): consumes the oldest lot first per sell. --method=average: pools all priced acquisitions; sells out at the running weighted average.

Both methods preserve the "honest unknown" rule: tokens received via wallet transfer-in (no price) produce realizedPnl: null for any sell drawing from them — NOT a fabricated number. The realized PnL counts only sales whose every consumed lot had a known cost basis.

Time-windowed PnL

headless-tracker show pnl --timeframe=7d
headless-tracker show pnl --timeframe=24h
headless-tracker show pnl --timeframe=ytd

Values your current basket at historical CoinGecko prices and reports the delta vs. now. Approximation: it does NOT account for trades within the window — it answers "if I held this exact basket N days ago, how much have I gained?" Polymarket positions and crypto without a CoinGecko mapping are skipped (counted in skippedSymbols). CoinGecko free-tier historical is daily granularity, so --timeframe=24h is "yesterday's close vs now".

Custom ERC-20 tokens

The bundled MetaMask token list covers USDC, USDT, WETH, WBTC, LINK, DAI. To track project-specific tokens:

headless-tracker token add metamask:0xabc 1 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 USDC 6
headless-tracker token list
headless-tracker token remove metamask:0xabc 1 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48

Custom tokens are stored per-account in the SQLite account store (NOT the keychain — they're public on-chain identifiers, not secrets).

Supported integrations

Connector

Auth

Status

Notes

Bybit V5

API key + secret

✓ Full

UNIFIED / SPOT / CONTRACT / FUND accounts. Read-only key.

Binance

API key + secret

✓ Holdings

Spot account (/api/v3/account) + optional Futures wallet/positions (/fapi/v2/account). Read-only key ("Enable Reading" only). Stablecoins priced at $1; non-stable assets priced via batch /api/v3/ticker/24hr?type=MINI. Open futures positions surface as separate Holdings with side/leverage/PnL/liquidation in metadata. Tx history is ok([]) for v0.13 — deferred. binance.com only (binance.us deferred).

MetaMask / EVM wallets

Etherscan V2 API key

✓ Full

Single key covers Ethereum, Polygon, BSC, Base, Arbitrum, Optimism. Native + bundled common ERC-20 tokens (USDC, USDT, WETH, WBTC, LINK, DAI) for balances; native + ERC-20 transfers for transactions. Custom token lists via headless-tracker token add .... Multi-wallet per account (one Etherscan key, multiple addresses). BSC/Base require Etherscan Pro on the free tier (auto-skipped with a warning otherwise).

Polymarket

Proxy wallet address (no API key)

✓ Full

Uses public data-api. Positions + BUY/SELL trade history (up to ~1000 most recent) via /trades?user=PROXY. Settled-loss positions (resolved markets worth $0 that the data-api still returns) are filtered out by a value-based dust threshold (dustThresholdUsd, default $0.01).

Solana wallets

Base58 address (no API key)

✓ Holdings

Public Solana RPC + Jupiter Price API v2. Native SOL + SPL tokens (Token program v1; Token-2022 deferred). Multi-wallet per account, optional premium RPC URL (Helius/QuickNode/Triton) for users tracking 3+ wallets. Pinned metadata for major mints (USDC/USDT/mSOL/JUP/JTO/PYTH/BONK/RNDR/WIF/JLP). Tx history is ok([]) for v0.12 — coming in v0.14 with premium-RPC opt-in.

Hyperliquid

EVM address (no API key, no signature)

✓ Full

Public info endpoint. Perp account equity reported as the account's net USD value (collateral + unrealized PnL); open perp positions surface as separate Holdings with signed size, notional, unrealized PnL, entry/liquidation price and leverage in metadata — their notional is deliberately not summed into net worth (would overstate a leveraged account). Spot balances priced via spotMetaAndAssetCtxs USDC pairs; USDC = $1, unpriceable tokens dust-filtered. Tx history via recent fills (userFills, up to 2000). Multi-address per account.

To add a new connector, implement Connector from src/connectors/types.ts and add it to CONNECTOR_FACTORIES in src/mcp/orchestrator.ts. ~150-400 lines of code per connector based on the existing six (depends on whether the upstream API is REST/SDK/RPC and how rich the response shape is).

MCP tools exposed

Tool

Purpose

Common prompts

get_holdings

Current holdings across all accounts

"what do I own", "show my portfolio", "current positions"

get_pnl

Aggregate profit/loss summary

"how am I doing", "what's my P&L", "am I up or down"

get_polymarket_positions

Polymarket-specialized, event-grouped

"show my Polymarket bets", "election bets"

get_transactions

Transaction history with since filter

"show my recent trades", "transactions this week"

get_allocations

Group-by breakdown (asset class / connector / chain / symbol)

"how is my portfolio split", "biggest positions"

refresh_data

Force cache invalidation

"refresh", "get the latest", "fetch now"

The data tools accept an optional account_id filter (e.g. metamask:0xabc..., bybit:UNIFIED). Without a filter, they query everything.

Account and setup management (all credential writes are read-only API keys stored in the OS keychain):

Tool

Purpose

setup_connector

Configure a connector by writing read-only credentials to the OS keychain

list_accounts

List configured accounts without exposing credentials

add_wallet_address

Add another wallet address to an existing MetaMask or Solana account

remove_account

Delete an account and its credentials from the keychain

add_custom_token

Track a project-specific ERC-20 token on a MetaMask account

remove_custom_token

Stop tracking a custom ERC-20 token (public on-chain data, no keychain)

list_custom_tokens

List the custom ERC-20 tokens tracked per MetaMask account

MCP App panels (interactive UI rendered in the chat):

Tool

Purpose

render_dashboard

Interactive dashboard panel: holdings, P&L, allocations, prediction markets

render_settings

Settings panel: a GUI alternative to the CLI setup flow

Why local-first

  • API keys never leave your machine. Stored in your OS keychain via @napi-rs/keyring.

  • Cache is local SQLite (the runtime's built-in driver: node:sqlite under Node, bun:sqlite under Bun). No server, no SaaS, no analytics pings.

  • No telemetry by default. Error reporting is strictly opt-in: it only does anything if you set a SENTRY_DSN, and even then it never sends portfolio data — no amounts, balances, wallet addresses, API keys, or account labels, only the error class and a scrubbed message/stack plus which connector failed. See decisions.md (2026-06-06) for the details.

  • Read-only by design. No transaction signing. Nothing this tool can do can lose your money.

  • Per-connector cache TTL (crypto wallets 60s, exchanges 120s, Polymarket 30s) keeps things fast without hammering upstream APIs.

Architecture

              ┌────────────────────────────┐
              │     headless-tracker       │
              │      (Node or Bun)         │
              │  ────────────────────────  │
              │  src/connectors/           │
              │    bybit.ts                │
              │    metamask.ts             │
              │    polymarket.ts           │
              │  src/types.ts (schema)     │
              │  src/cache.ts (SQLite)     │
              │  src/vault.ts (keyring)    │
              │  src/accounts.ts (registry)│
              │  src/mcp/orchestrator.ts   │  ← parallel fan-out + in-flight Promise dedup
              │  src/mcp/server.ts         │  ← McpServer + 15 tools
              │       ▲                    │
              │       │ stdio MCP          │
              └───────┼────────────────────┘
                      │
       ┌──────────────┼──────────────┐
       │              │              │
┌──────▼──────┐ ┌─────▼─────┐ ┌──────▼──────┐
│Claude Desktop│ │Claude Code│ │ Cursor/Codex│
│  ChatGPT     │ │           │ │  ZED, etc.  │
└──────────────┘ └───────────┘ └─────────────┘

Sample prompts and responses

The point of headless-tracker is that you don't write SQL or learn a CLI — you ask Claude. Some example sessions:

"What do I own?"

Claude calls get_holdings({}) and returns a formatted breakdown: "You currently hold 0.5 BTC ($30,000), 2 ETH ($5,000) on Bybit, plus 1 ETH on your MetaMask wallet, and one Polymarket position on the 2024 election worth $60. Total portfolio value: ~$35,060."

"Show my Polymarket bets grouped by event."

Claude calls get_polymarket_positions({ group_by_event: true }) and renders an event-grouped table with title, your Yes/No outcome holdings, total event value, and combined cash P&L per event.

"How am I split between crypto and prediction markets?"

Claude calls get_allocations({ by: "asset_class" }) and returns a percentage breakdown: "97.5% crypto ($35,000), 2.5% prediction ($1,000)."

"Refresh my data and show the latest holdings."

Claude calls refresh_data({}) then get_holdings({}) — the cache is invalidated, fresh data is fetched from all upstream APIs in parallel, and Claude renders the new state.

"Give me a complete portfolio dashboard."

Claude calls multiple tools in parallel (get_holdings, get_allocations, get_pnl, get_polymarket_positions) and synthesizes a multi-section dashboard. The orchestrator's in-flight Promise dedup ensures each connector is hit at most once even when fan-out is wide.

Development

Building from source uses Bun 1.3+. End users don't need this — see Quick start.

git clone https://github.com/tamasPetki/HeadlessTracker.git
cd headless-tracker
bun install
bun test                              # 377 tests, ~5s
bun run typecheck                     # bun --bun tsc --noEmit
bun run build:apps                    # bundle the dashboard MCP App into dist/mcp-apps/
bun run build                         # build the Node-runnable dist/ (what npm ships)
bun run start                         # start MCP server on stdio (debug only)
bun run setup bybit                   # interactive credential setup

To add a connector, follow the existing pattern in src/connectors/. The Connector interface enforces uniform Result<T> error handling across all integrations — there is no exception-throwing path for expected failures (auth, rate limit, network).

FAQ

Will my API keys be sent anywhere? No. They're stored in your OS keychain and only sent to the upstream API they're for (Bybit's API for Bybit keys, Etherscan's API for Etherscan keys). The MCP server runs entirely on your machine.

Polymarket has my positions but Claude can't see them. Make sure you used your Polymarket proxy wallet address, not your MetaMask address. Find it in the Polymarket UI under Settings → Wallet. Re-run setup polymarket if you used the wrong one.

Bybit returns auth_failed. Verify the API key has Wallet Read + Trade Read permissions (NO Withdraw needed). If you set an IP whitelist on the key, make sure your machine's current public IP is on it.

Etherscan returns rate_limited. The free tier is 5 calls/sec, 100k/day. Each MetaMask refresh costs (1 + N tokens) calls per chain. If you have 4 chains × 7 common tokens, that's 32 calls per refresh. Spread your refresh requests; the cache TTL (60s for MetaMask) is there for a reason.

Can I use this without Claude Desktop? Yes — any MCP-compatible host works (Claude Code, Cursor, Codex, ZED, ChatGPT once their MCP support stabilizes). Wire it the same way; just change which client config file you edit. There's also a CLI (headless-tracker show holdings/pnl/transactions) for terminal queries that don't need an AI host at all.

Realized P&L looks wrong. For Polymarket, default get_pnl returns realizedPnl: null because the connector's cashPnl field mixes realized + unrealized. Pass include_history=true (or --include-history=true from the CLI) to get the honest number computed from FIFO over your /trades history. For MetaMask, on-chain transfer-in tokens have no known cost basis — include_history=true reports them as unknownSalesCount instead of fabricating $0.

Can one MetaMask account track multiple wallets? Yes. v0.8 added addresses[] to MetaMask credentials. The setup CLI still asks for one wallet (back-compat), but the connector accepts a list. Edit the vault entry directly to add more addresses to the same account, sharing the same Etherscan key + chain selection. A wallet add CLI command is on the v0.9 list if there's demand.

Does this support [my favorite exchange / chain / market]? Not yet. Open an issue or PR. The Connector interface is open for extension and the existing 3 connectors are reference implementations totaling ~600 lines.

What about transaction history for Polymarket? Supported as of v0.7.1: get_transactions returns BUY/SELL trades from the public data-api /trades?user=PROXY endpoint, up to ~1000 most recent. The data-api ignores time-bound query params, so the since filter is enforced client-side; pagination terminates early once a page falls before the cutoff.

Why no UI? Claude Desktop, ChatGPT, and Cursor all generate richer dashboards on demand than I'd ship in v1. Building a UI duplicates work the AI host already does better. If you want a hosted web UI, see bulltrapp.com.

bulltrapp.com — a hosted web portfolio tracker by the same maintainer. Same problem space, different surface. Use either or both.

License

MIT

Available Tools

15 tools
add_custom_tokenA

Adds a project-specific ERC-20 token to a MetaMask account's tracked-tokens list. The bundled common tokens (USDC, USDT, WETH, WBTC, LINK, DAI) are tracked by default; this is for additional tokens like project / governance tokens. Use when the user asks: 'track ARB token', 'add UNI to my wallet', 'monitor a custom ERC-20'. Token data is PUBLIC on-chain (contract + symbol + decimals) — NO secrets, NO keychain involved. Inputs: - account_id: target MetaMask account (e.g. 'metamask:0xabc...') - chain_id: numeric (1=Ethereum, 137=Polygon, 56=BSC, 8453=Base, 42161=Arbitrum, 10=Optimism) - contract: 0x-prefixed contract address (40 hex) - symbol: 1-20 chars - decimals: 0-36 integer

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idYesTarget MetaMask account id (e.g. 'metamask:0xabc...').
chain_idYesEVM chain id: 1=Ethereum, 137=Polygon, 56=BSC, 8453=Base, 42161=Arbitrum, 10=Optimism.
contractYesERC-20 contract address: 0x-prefixed, 40 hex chars. Public on-chain data, no secret.
symbolYesToken ticker symbol, 1-20 chars (e.g. 'ARB').
decimalsYesToken decimals (integer 0-36; most ERC-20s use 18, USDC/USDT use 6).

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden and discloses that token data is public on-chain with no secrets or keychain involvement, reassuring about safety and side effects. It also implies the operation is additive (no destruction).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a well-structured paragraph that efficiently covers purpose, usage, privacy, and all inputs without redundancy. Every sentence adds value, and it is appropriately front-loaded with the core action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (5 required params, no output schema), the description thoroughly explains the tool's purpose, when to use, each parameter with examples, and security context. It is complete for an agent to select and invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with descriptive parameter names and constraints. The description adds extra context like typical decimal values (18, 6) and clarifies contract address is public, enhancing understanding beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it adds a project-specific ERC-20 token to a MetaMask tracked-tokens list, distinguishes from default tokens, and gives concrete use-case examples like 'track ARB token'. It differentiates from sibling tools (list_custom_tokens, remove_custom_token).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states 'Use when the user asks: ...' and implies common tokens are already tracked, so not for those. Provides scenarios and contrasts with default tracked tokens, giving clear when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

add_wallet_addressA

Adds an additional wallet address to an existing MetaMask or Solana account. Use when the user asks: 'add another wallet', 'track a second address', 'add MetaMask address', 'add Solana address'. The new address shares the parent account's settings (Etherscan key + chains for MetaMask; RPC URL for Solana). Wallet addresses are public on-chain identifiers — NO new secrets are stored. Just updates the keychain entry's addresses[] field. Inputs: - account_id: id of the existing account (e.g. 'metamask:0xabc...' or 'solana:Abc...') - address: EVM 0x-hex address (40 hex chars) for MetaMask accounts, or base58 (32-44 chars) for Solana accounts

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idYesId of the existing MetaMask or Solana account to add the address to (e.g. 'metamask:0xabc...' or 'solana:Abc...'). The new address inherits the parent's settings.
addressYesThe wallet address to add: an EVM 0x-hex address (40 hex chars) for MetaMask accounts, or a base58 address (32-44 chars) for Solana accounts. Public on-chain identifier, no secret.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full weight. It explains that the new address shares parent settings, no secrets are stored, and it just updates the addresses[] field. Good behavioral disclosure for a non-destructive operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficient, front-loading the core purpose and usage hints, then detailing behavior and parameter specifics. Every sentence adds value without fluff or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description explains what gets updated (addresses[] field) and notes security (no secrets). It could mention error handling (e.g., if account doesn't exist or address already present) but covers most essential aspects.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds significant value by clarifying each parameter's role, providing examples (e.g., 'metamask:0xabc...'), and specifying address formats (0x-hex for EVM, base58 for Solana). This goes beyond the schema's basic descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it adds a wallet address to an existing MetaMask or Solana account, using a specific verb and resource. It distinguishes from siblings like add_custom_token or setup_connector.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly lists example user queries that trigger use, such as 'add another wallet' or 'track a second address'. However, it does not specify when not to use or mention alternative tools, though the sibling list provides context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_allocationsA

Returns portfolio allocation breakdown — current USD value grouped by a chosen dimension. Use this when the user asks: 'how is my portfolio split', 'allocation breakdown', 'what % is in crypto', 'chain breakdown', 'show my biggest position', 'concentration'. Groups available: - 'asset_class' (default): crypto / stock / prediction / cash - 'connector': bybit / metamask / polymarket - 'account': one row per configured account - 'chain': EVM chain (Ethereum / Polygon / etc.) — only meaningful for MetaMask holdings - 'symbol': BTC / ETH / individual market — best for top-N concentration analysis Each group row includes: label, currentValue (USD), percentOfTotal, holdingCount. Sorted descending by currentValue. Inputs: - by: which dimension to group by (see above). Default 'asset_class'. - top: limit to top N rows (e.g. top: 10 for biggest positions). Default no limit. Returns position data only. Not financial advice.

ParametersJSON Schema
NameRequiredDescriptionDefault
byNoDimension to group USD value by (default 'asset_class'). 'chain' is only meaningful for MetaMask holdings; 'symbol' is best for top-N concentration analysis.
topNoLimit to the top N rows by value (e.g. 10 for biggest positions). Omit for no limit.

TDQS

A4.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It does not explicitly state the tool is read-only or mention side effects, auth requirements, or rate limits. The description focuses on functionality and output, but a statement about safety would improve transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise at ~150 words, well-structured with a clear first sentence, followed by use cases, grouping options, output format, and parameter details. Every sentence adds value without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema, the description specifies the return fields (label, currentValue, percentOfTotal, holdingCount) and sort order. The two parameters are fully explained. The tool is simple and well-documented for an agent to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage, but the description adds significant value by explaining each enum option's meaning (e.g., 'chain' is only meaningful for MetaMask) and the default for 'by'. It also clarifies the 'top' parameter behavior, going beyond the schema's minimal description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns a portfolio allocation breakdown grouped by a chosen dimension, with specific verb 'Returns' and resource 'portfolio allocation breakdown'. It distinguishes itself from sibling tools like get_holdings by focusing on aggregated breakdowns rather than raw positions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly provides example queries for when to use the tool, such as 'how is my portfolio split' and 'allocation breakdown'. It also details each grouping dimension and their context (e.g., 'chain' only for MetaMask). However, it does not explicitly contrast with sibling tools or state when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_holdingsA

Returns current portfolio holdings across all connected accounts. Use this when the user asks: 'what do I own', 'show my portfolio', 'current positions', 'what's my balance', 'show my holdings', 'how much BTC do I have', or wants any snapshot of crypto/stock/prediction-market positions. Each holding includes: symbol, asset class (crypto / stock / prediction / cash), quantity, current price, USD value, and connector-specific metadata (e.g. chain for EVM, market title for Polymarket, accountType for Bybit). Inputs (all optional): - account_id: scope to one account, e.g. 'metamask:0xabc123...' or 'bybit:UNIFIED'. Omit to query ALL configured accounts. - asset_class: scope to one of 'crypto' / 'stock' / 'prediction' / 'cash'. 'prediction' = Polymarket conditional tokens. Omit for all classes. - currency: 'USD' | 'EUR' | 'GBP' | 'HUF'. Default 'USD'. When set to anything other than USD, value/currentPrice/avgCost are converted via live FX rates (free API + fallback). The conversion source and fetchedAt are surfaced in meta.fx. Underlying connector data is unchanged — this is a display-layer convenience for users thinking in EUR/HUF/GBP. Results are cached per-connector (crypto wallets 60s, exchanges 120s, Polymarket 30s). Use 'refresh_data' tool first if the user explicitly asks for fresh / latest data. Returns position data only. Not financial advice.

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idNoScope to one account by id, e.g. 'metamask:0xabc123...' or 'bybit:UNIFIED'. Omit to query ALL configured accounts. Get valid ids from list_accounts.
asset_classNoScope to one asset class. 'prediction' = Polymarket conditional tokens; 'cash' = stablecoin/fiat balances. Omit for all classes.
currencyNoDisplay currency for value/currentPrice/avgCost (default 'USD'). Non-USD converts via live FX; the rate and source appear in meta.fx. Display-only — underlying data is unchanged.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description fully carries the burden. Discloses caching times per connector, notes that non-USD conversion is a display-layer convenience, and states that underlying data is unchanged. Could be slightly improved by mentioning idempotency, but is thorough.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with bullet points and clear sections. Every sentence adds value, though slightly verbose for an AI agent. Front-loaded with purpose and examples.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Completes all needed context: purpose, parameters, return structure, caching, disclaimers. No output schema but description details what each holding includes. Sufficient for agent to decide and invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3. The description adds meaningful context: explains omitting account_id queries all, provides example formats, defines asset_class special values, and clarifies currency conversion behavior. Adds significant value beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns current portfolio holdings across all connected accounts. It lists specific user queries that trigger this tool, distinguishing it from siblings like get_allocations, get_pnl, and get_polymarket_positions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly advises when to use (user asks about holdings) and when to use an alternative (use refresh_data for fresh data). Provides clear context and excludes financial advice.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_pnlA

Returns aggregate profit/loss across all configured accounts. Use this when the user asks: 'how am I doing', 'what's my P&L', 'am I up or down', 'show profit', 'show losses', or wants any portfolio performance summary. Returned fields per account and across the total: - currentValue (USD) — current portfolio value - costBasis (USD) — sum of avgCost * quantity (where the connector tracks it) - unrealizedPnl (USD) — currentValue - costBasis (for positions still held) - realizedPnl (USD) — already-closed P&L from connector metadata - notes — caveats per connector (e.g. MetaMask doesn't track cost basis) Inputs (optional): - account_id: scope to one account. - timeframe: '24h' | '7d' | '30d' | 'ytd' | 'all'. When set to anything other than 'all', the result includes a windowDelta block computed from CoinGecko historical prices. APPROXIMATION CAVEAT: it values your CURRENT basket at historical prices vs current prices — it does NOT account for trades within the window. Communicate this honestly to the user. Polymarket positions and tokens without a CoinGecko mapping are skipped (counted in skippedSymbols). CoinGecko free-tier historical is daily granularity, so '24h' = 'yesterday's close'. - include_history (boolean, default false): also pulls transactions and runs a cost-basis ledger over them, returning realizedFromHistory per account + total. Costs an extra round-trip per account but unlocks honest realized PnL on tokens born on-chain (LP rewards, swaps, native airdrops). Tokens that arrived via wallet transfer-in (no price) get unknownSalesCount not inflated knownRealized — explicit honesty about what cost basis we know. POLYMARKET-SPECIFIC: when include_history=true, the Polymarket account's realizedPnl is replaced by the cost-basis-from-/trades number. Default mode leaves Polymarket realizedPnl null because the connector's cashPnl mixes realized + unrealized — set include_history=true to get the real realized number. - method ('fifo' | 'average', default 'fifo'): cost basis method used when include_history=true. FIFO consumes oldest lot first per sell; Average Cost pools all priced acquisitions and sells out at the running average. If the user mentions 'average cost' / 'avg cost' / 'weighted', use 'average'. Both methods preserve the 'honest unknown' rule: any sell drawing from an unpriced deposit/transfer returns realizedPnl=null, NOT a fabricated number. Has NO effect when include_history=false. - currency ('USD' | 'EUR' | 'GBP' | 'HUF', default 'USD'): when set to anything other than USD, ALL numeric fields (currentValue, costBasis, realizedPnl, unrealizedPnl, windowDelta numbers, realizedFromHistory.knownRealized) are converted via live FX rates. The fx.source + fetchedAt are surfaced in meta.fx. Use this for currency-consistent rendering when the user asked their dashboard to be in HUF/EUR/GBP — otherwise per-tab currencies will mismatch. Returns position data only. Not financial advice.

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idNoScope to one account (e.g. 'bybit:UNIFIED'). Omit for P&L across all accounts.
timeframeNoDefault 'all'. Any non-'all' value adds a windowDelta block (current basket valued at historical vs current CoinGecko prices). APPROXIMATION: it does NOT account for trades within the window; surface that caveat. Daily granularity, so '24h' = since yesterday's close.
include_historyNoDefault false. When true, also fetches transactions and runs a cost-basis ledger for honest realizedFromHistory per account (one extra round-trip each). Required to get Polymarket's real realized P&L (null otherwise).
methodNoCost-basis method when include_history=true (default 'fifo'). Use 'average' if the user says 'average/avg/weighted cost'. No effect when include_history=false.
currencyNoDisplay currency for all numeric fields (default 'USD'). Non-USD converts via live FX; source/rate appear in meta.fx. Use for currency-consistent dashboards.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully covers behavioral traits: approximation caveats for windowDelta, CoinGecko limitations, skipping Polymarket and tokens without mapping, extra round-trips for include_history, cost basis method effects, and currency conversion with source disclosure. It also includes a disclaimer.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very long and detailed, which can hinder quick parsing by an AI agent. While it is well-structured with front-loaded purpose, the verbosity could be reduced for better conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description thoroughly explains return fields and caveats (currentValue, costBasis, realizedPnl, etc.). It covers edge cases like Polymarket, unknown cost basis, and currency conversion, making it complete for a tool of this complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but the description adds significant value beyond the schema for each parameter: account_id omitting for cross-account, timeframe approximation and granularity, include_history trade-offs, method selection based on user language, and currency conversion details. This far exceeds baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states it returns aggregate profit/loss across all accounts and gives specific user queries that trigger its use (e.g., 'how am I doing', 'what's my P&L'). It clearly distinguishes from sibling tools like get_holdings and get_transactions by focusing on P&L summary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit triggers for when to use the tool and detailed guidelines for parameters (e.g., when to set include_history, timeframe, method, currency). It does not explicitly list when not to use it, but the context implies alternatives exist for specific positions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_polymarket_positionsA

Returns Polymarket prediction-market positions, grouped by event when possible. Use this when the user asks about their Polymarket bets specifically: 'show my Polymarket positions', 'how am I doing on Polymarket', 'what bets do I have', 'show my prediction market positions', 'election bets', 'sports bets', etc. For general portfolio queries that mention Polymarket alongside crypto/stocks, prefer get_holdings (with optional asset_class='prediction' filter). Each position includes: - market title (human-readable question, e.g. 'Will X win the 2024 election?') - outcome ('Yes' / 'No' / specific candidate name) - quantity (conditional tokens held, each worth 0-1 USDC) - currentPrice (0-1, market's current implied probability) - currentValue (USD), avgCost (USD per token), cashPnl (realized + unrealized) - redeemable (true if market resolved and you can claim payout) - mergeable (true if you can merge Yes+No tokens for guaranteed USDC) - endDate (when the market resolves) Inputs (optional): - account_id: scope to one Polymarket account (if you have multiple). - group_by_event: 'true' (default) groups Yes+No outcomes for the same market; 'false' returns one row per asset. - resolved_only: 'true' returns only redeemable positions (markets that have resolved). Default 'false' returns everything. Returns position data only. Not financial advice.

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idNoScope to one Polymarket account if several are configured. Omit for all.
group_by_eventNoDefault true: groups Yes+No outcomes of the same market into one event row. Set false for one row per asset/outcome.
resolved_onlyNoDefault false (return all positions). Set true to return only redeemable positions in markets that have already resolved.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of transparency. It details the output fields (market title, outcome, quantity, currentPrice, etc.), explains defaults for optional parameters, and includes a disclaimer ('Not financial advice'). It does not mention authentication or rate limits, but the behavioral description is thorough and consistent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured: purpose statement first, then usage guidance, then output field details, then parameter descriptions. Every sentence adds necessary information; there is no redundancy. It is appropriately sized and front-loaded with the key purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite lacking an output schema, the description compensates by thoroughly enumerating the returned fields. It covers inputs and outputs adequately for an AI agent to understand the tool's behavior. Minor omissions (e.g., error handling, rate limits) prevent a perfect score.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so each parameter already has a description. The description adds extra value by explaining defaults (group_by_event defaults true, resolved_only defaults false) and provides context on when to use each parameter. This goes beyond the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool returns Polymarket prediction-market positions grouped by event. It provides specific example queries and distinguishes itself from the sibling tool get_holdings, which is intended for general portfolio queries. The verb 'returns' and resource 'Polymarket positions' are precise.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells when to use this tool ('when the user asks about their Polymarket bets') with concrete examples, and when not to use it ('for general portfolio queries...prefer get_holdings'). It also suggests an alternative filter for get_holdings. This is exemplary guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_transactionsA

Returns transaction history (trades, deposits, withdrawals, fees, ERC-20 token transfers) across configured accounts. Use this when the user asks: 'what trades did I make', 'show my transactions', 'transaction history', 'recent buys', 'recent sells', 'show my deposits', 'when did I buy X', 'what did I do this week', etc. Each transaction includes: - type: trade / buy / sell / deposit / withdraw / fee / interest / reward / transfer / resolve - symbol, quantity, price (per-unit at time of transaction) - fee + feeCurrency - timestamp (ISO 8601) - metadata (connector-specific: chain, hash, market, etc.) Inputs (optional): - account_id: scope to one account. - since: shorthand ('24h', '7d', '30d', '90d') OR epoch ms (e.g. 1700000000000). Default: ~last 50 transactions per account regardless of date. Coverage caveats (V0): - Bybit: full transaction log - MetaMask: native chain transfers + ERC-20 token transfers (USDC, USDT, etc.) - Polymarket: BUY/SELL trades from the /trades endpoint (up to ~1000 most recent). Returns transaction data only. Not financial advice.

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idNoScope to one account (e.g. 'metamask:0xabc...'). Omit for all accounts.
sinceNoLower time bound: shorthand ('24h', '7d', '30d', '90d') or epoch milliseconds as a string (e.g. '1700000000000'). Omit for roughly the last 50 transactions per account regardless of date.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses return format, default behavior (last 50 transactions per account), and limitations per connector. It lacks an explicit read-only assertion, but the informative nature compensates.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a clear purpose first, followed by usage, output format, inputs, and caveats. It is slightly verbose but every section adds information; could be trimmed slightly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Absent an output schema, the description fully explains the return fields (type, symbol, quantity, etc.) and covers connector-specific behavior. It includes necessary caveats (V0, not financial advice) making it complete for the tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the description adds value by providing examples (e.g., time shorthand '24h', account_id format) and elaborating on default behavior. This goes beyond the schema's basic descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states it returns transaction history across configured accounts, listing types (trades, deposits, etc.) and providing example user queries. This clearly distinguishes it from siblings like get_holdings or get_pnl.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage examples ('Use this when the user asks: ...') and covers connector-specific caveats. However, it does not explicitly state when not to use this tool in favor of alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_accountsA

Lists configured PORTFOLIO TRACKER accounts (Bybit exchange / Binance exchange / MetaMask wallets / Polymarket / Solana wallets) without exposing credentials. Use when the user asks: 'what tracker accounts are configured', 'show my portfolio accounts', 'list my exchange connections', 'which crypto exchanges are linked'. Returns: id, connectorId (bybit | binance | metamask | polymarket | solana), label, createdAt, and connector-specific public metadata (e.g. chainIds and addresses for MetaMask, accountType for Bybit, address for Solana, key fingerprint for Binance). Credentials are NEVER returned — they stay in the OS keychain. DO NOT call this tool when the user means: email accounts, social media accounts, GitHub accounts, cloud accounts, OS user accounts, or any 'accounts' from a different domain or MCP server. It's specifically the headless-tracker exchange/wallet connections.

ParametersJSON Schema
NameRequiredDescriptionDefault
connectorNoFilter to accounts of one connector. Omit to list every configured account.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite no annotations, the description fully discloses that credentials are never returned and stay in the OS keychain, and lists the exact fields returned (id, connectorId, label, createdAt, connector-specific metadata).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is informative with multiple sentences, but all content is necessary. Front-loaded with the tool's purpose and examples. Slightly verbose but no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one optional parameter, list operation), the description fully covers behavior, return fields, and usage boundaries. Without an output schema, the description compensates with detailed return info.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The optional 'connector' parameter is explained in the description (omit for all accounts, include to filter), adding context beyond the enum list. Schema coverage is 100%, so baseline 3; the description adds clear usage guidance.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it lists configured PORTFOLIO TRACKER accounts (specific exchange/wallet types) and distinguishes from sibling tools by explicitly excluding unrelated account types (email, social media, etc.).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit usage examples (user queries) and states when NOT to use the tool (other account domains), giving clear guidance on when to invoke this tool vs alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_custom_tokensA

Lists custom ERC-20 tokens tracked by MetaMask accounts. Public data; no secrets involved. Use when the user asks: 'which custom tokens am I tracking', 'show my project tokens'. Inputs (optional): account_id to filter to one account.

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idNoFilter to one MetaMask account id. Omit to list custom tokens across all accounts.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It explicitly states 'Public data; no secrets involved,' which is helpful for safety. It does not mention any destructive behavior or side effects, but for a read-only list operation, this is adequate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with two sentences, front-loading the main purpose. It includes usage examples and parameter explanation without unnecessary verbosity. Every sentence serves a purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with one optional parameter and no output schema, the description is fairly complete. It covers purpose, usage, safety, and parameter behavior. However, it does not describe the return format or any pagination, but these may be implied.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with one parameter. The description adds value by explaining that omitting 'account_id' lists across all accounts, which is not explicitly stated in the schema. This provides context beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states what the tool does: 'Lists custom ERC-20 tokens tracked by MetaMask accounts.' It uses a specific verb ('lists') and resource ('custom ERC-20 tokens'). This distinguishes it from sibling tools like 'add_custom_token' and 'remove_custom_token' which perform different actions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides example user queries ('which custom tokens am I tracking', 'show my project tokens') to guide when to use the tool. It also mentions 'Public data; no secrets involved' indicating safety. However, it does not explicitly state when not to use it or suggest alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

refresh_dataA

Invalidates the PORTFOLIO TRACKER cache (Bybit/Binance/MetaMask/Polymarket/Solana holdings + transactions) and forces a fresh fetch from upstream APIs on the next call. Use this when the user asks: 'refresh my portfolio', 'update my holdings', 'get the latest portfolio prices', 'fetch fresh portfolio data', 'check current crypto prices', or implies real-time freshness is required FOR THE TRACKER. Optionally scope to a single connector (bybit, binance, metamask, polymarket, solana). Without a scope, invalidates everything tracker-related. DO NOT call this tool when the user means: refreshing a webpage, refreshing an OAuth token, refreshing browser cache, refreshing data from a non-tracker MCP server. It only invalidates the headless-tracker SQLite cache. After calling this, follow up with get_holdings or another data tool to actually fetch the fresh data — refresh_data only marks the cache as stale, it does not trigger fetches on its own.

ParametersJSON Schema
NameRequiredDescriptionDefault
connectorNoScope the cache invalidation to one connector. Omit to invalidate every tracker connector's cached holdings and transactions.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully carries the burden of behavioral disclosure. It explains that the tool only invalidates the cache, does not trigger fetches, and optionally scopes to a single connector. It also warns of the scope without a parameter (invalidates everything tracker-related).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with front-loaded key information and clear sections. However, it is somewhat lengthy (5 sentences) and could be slightly more concise without losing meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description does not mention return values or status indications. However, it covers usage, scope, follow-up, and exclusions comprehensively. Missing return value info is a minor gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% coverage with one parameter (connector) enumerated. The description adds value by explaining that omitting the parameter invalidates all connectors, and providing context about connector options (bybit, binance, metamask, etc.).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool invalidates the PORTFOLIO TRACKER cache and forces a fresh fetch on next call, with specific examples of user queries ('refresh my portfolio', 'update my holdings', etc.). It distinguishes from sibling tools by focusing on cache invalidation rather than data retrieval (e.g., get_holdings).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use (user asks for portfolio refresh/update/fetch) and when not to use (refreshing webpage, OAuth token, browser cache, non-tracker MCP server). Also provides follow-up guidance to call get_holdings after invalidation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

remove_accountA

Deletes an account from the AccountStore AND its credentials from the OS keychain. ONE-WAY operation. The Settings UI requires explicit user confirmation before calling this. Use when the user asks: 'remove the Bybit account', 'disconnect Polymarket', 'forget that wallet'. Inputs: - account_id: id of the account to remove (e.g. 'bybit:UNIFIED', 'metamask:0xabc...'). Returns: ok, removedAccountId; or error if the account was not found.

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idYesId of the account to delete (e.g. 'bybit:UNIFIED', 'metamask:0xabc...'). ONE-WAY: removes the account and its keychain credentials. Confirm with the user first.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Since no annotations are provided, the description fully carries the burden. It discloses the operation is ONE-WAY, deletes from AccountStore and OS keychain, and specifies return values and error conditions. This is thorough and honest.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is informative but could be more streamlined. It includes usage examples, input details, and returns in a natural flow, but some redundancy exists (e.g., repeating 'ONE-WAY'). Still, it is not overly verbose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations or output schema, the description covers the essential aspects: irreversible operation, target, return values, and error case. It could mention prerequisites like user authentication, but overall it is comprehensive enough for a single-parameter tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by providing format examples (e.g., 'bybit:UNIFIED') and reiterating the one-way nature, which goes beyond the schema's simple description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it deletes an account and its credentials, and provides specific example phrases like 'remove the Bybit account' that cue the agent. It distinguishes from sibling tools like remove_custom_token by focusing on accounts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance by listing user queries. It mentions the Settings UI requires confirmation, implying appropriate timing. However, it does not explicitly contrast with alternatives like remove_custom_token or note when not to use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

remove_custom_tokenA

Removes a custom ERC-20 token from a MetaMask account's tracked-tokens list. Public on-chain data, no keychain involvement. Use when the user asks: 'stop tracking ARB', 'remove that token from MetaMask', 'untrack a project token'. Inputs: - account_id: target MetaMask account (e.g. 'metamask:0xabc...') - chain_id: numeric (1=Ethereum, 137=Polygon, 56=BSC, 8453=Base, 42161=Arbitrum, 10=Optimism) - contract: 0x-prefixed contract address (40 hex) Returns ok or a not_found error if the token wasn't tracked under that account+chain.

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idYesTarget MetaMask account id (e.g. 'metamask:0xabc...').
chain_idYesEVM chain id the token is tracked on: 1=Ethereum, 137=Polygon, 56=BSC, 8453=Base, 42161=Arbitrum, 10=Optimism.
contractYesERC-20 contract address to untrack: 0x-prefixed, 40 hex chars.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. Discloses 'Public on-chain data, no keychain involvement' and describes return behavior (ok or not_found error). Sufficiently transparent for a simple removal operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Concise, well-structured description at about 100 words. Includes overview, usage examples, parameter list, and return info without extraneous content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple 3-param tool with no output schema and no annotations, the description fully covers purpose, usage, parameter details, and error handling. No gaps identified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and description adds value beyond schema: provides example for account_id, lists common chain_id values, and specifies contract address format. Also explains return behavior, which is not in schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states it removes a custom ERC-20 token from a MetaMask tracked-tokens list. Provides specific verb and resource, and example user queries. Distinguishes from sibling tools like add_custom_token and list_custom_tokens.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use when the user asks...' with concrete examples (e.g., 'stop tracking ARB'). Does not explicitly mention when not to use, but the examples make it clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

render_dashboardPortfolio DashboardA

Render an interactive PORTFOLIO TRACKER dashboard as an MCP App (live UI panel) — for crypto holdings, P&L, prediction markets, on-chain wallets. Use this when the user asks: 'show my portfolio dashboard', 'open my dashboard', 'render the portfolio panel', or wants a live interactive view of holdings / weekly portfolio review / risk audit. Three tabs (Portfolio / Weekly / Risk) with currency switcher (USD/EUR/GBP/HUF) and refresh button. The iframe makes its own tool calls (get_holdings / get_pnl / get_allocations / get_transactions / get_polymarket_positions / refresh_data) as the user clicks tabs and refresh — no extra prompting from you needed once it's open. If the host doesn't render MCP Apps, the tool still returns a textual confirmation but the user won't get the live UI. DO NOT call this tool when the user means a different kind of dashboard (Vercel deploys, Sentry errors, Grafana metrics, GitHub activity, analytics events, etc.). It's specifically the headless-tracker portfolio dashboard. If the request is ambiguous, ask the user to clarify which dashboard they mean. Inputs (both optional): - currency: 'USD' | 'EUR' | 'GBP' | 'HUF'. Initial display currency. User can switch live. - tab: 'portfolio' | 'weekly' | 'risk'. Initial active tab. User can switch live.

ParametersJSON Schema
NameRequiredDescriptionDefault
currencyNoInitial display currency for the dashboard (default 'USD'). The user can switch it live in the panel.
tabNoInitial active tab (default 'portfolio'). The user can switch tabs live in the panel.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite no annotations, the description reveals key behaviors: the tool returns a textual confirmation if MCP Apps are not supported, the iframe makes its own tool calls, and parameters are initial only (user can switch live). It lacks mention of authentication or rate limits, but for a render tool these are not critical.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is relatively long but well-structured, with front-loaded purpose and clear sections. Every sentence adds value, though some repetition (e.g., 'User can switch live' said twice) could be tightened.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (2 optional params, no output schema, no annotations), the description is thorough: it covers purpose, usage, behavior, param details, and fallback behavior. No critical gaps remain.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds context to both parameters beyond the schema: it clarifies that they are initial values and users can switch live. The enum values are listed and defaults are implied. This adds significant value for an agent.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it renders a portfolio tracker dashboard with specific tabs and features. It distinguishes from other types of dashboards and specifies exact user prompts that should trigger this tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use ('show my portfolio dashboard', etc.) and when not to use (other dashboards like Vercel, Sentry, etc.). It advises to ask the user for clarification if ambiguous, and explains that the iframe handles its own tool calls, reducing agent burden.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

render_settingsSettingsA

Render the PORTFOLIO TRACKER Settings panel as an MCP App (live UI panel) — the GUI alternative to the CLI setup flow for headless-tracker (Bybit / MetaMask / Polymarket connections). Use when the user asks: 'open my portfolio settings', 'add a Bybit account', 'connect my MetaMask wallet to the tracker', 'show my tracker accounts', 'remove this exchange connection', 'add a custom ERC-20 token', 'add another wallet address to track'. Four tabs: - Accounts: read-only list of configured tracker accounts with Remove buttons (one-way confirm dialog). - Add Account: forms for Bybit / MetaMask / Polymarket with explicit security disclosure. - Wallets: add an additional wallet address to an existing MetaMask tracker account. - Custom Tokens: list + add/remove ERC-20 tokens per chain. DO NOT call this tool when the user means: app settings (Claude Desktop / VS Code / browser), system preferences, OS settings, account settings on websites, profile settings, notification settings, theme/appearance, or any settings UI from a different MCP server. It's specifically the headless-tracker portfolio-tracker setup panel. If the request is ambiguous (just 'open settings'), ask which settings. BEHAVIOR CONTRACT FOR YOU (the LLM): If the user pastes credentials directly in chat, prefer pointing them at this Settings UI rather than calling setup_connector with the inline values — the form keeps secrets out of the conversation transcript. After the user uses the form, the tool result is cosmetic confirmation; do NOT echo or paraphrase any credential values that may appear in the conversation. Inputs (optional): - tab: 'accounts' | 'add-account' | 'wallets' | 'tokens' — initial active tab.

ParametersJSON Schema
NameRequiredDescriptionDefault
tabNoInitial active tab (default 'accounts'). The user can switch tabs live in the panel.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavioral traits: it's a live UI panel with tabs, includes security disclosures, and instructs not to echo credentials, providing a behavior contract.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is somewhat lengthy but well-structured, with front-loaded purpose and clear sections. Every sentence adds value, though slight trimming could improve conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations or output schema, the description covers all essential aspects: inputs, UI layout, security notes, use cases, and exclusions, leaving no gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'tab' is fully described with enum; the description adds default value and live-switching behavior, going beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool renders the PORTFOLIO TRACKER Settings panel, listing specific user queries it handles, and differentiates from sibling tools like render_dashboard and setup_connector.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly specifies when to use (portfolio settings, adding accounts) and when not to (app settings, system preferences), with guidance to ask for clarification if ambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

setup_connectorA

Creates a new account by writing READ-ONLY credentials to the OS keychain. Use when the user asks: 'add a Bybit account', 'connect Binance', 'connect my MetaMask wallet', 'set up Polymarket', 'connect my Solana wallet', 'add new exchange'. BEHAVIOR CONTRACT FOR YOU (the LLM): - After this tool succeeds, confirm ONLY the account label and account_id back to the user. - NEVER echo, log, paraphrase, or repeat the credential values (apiKey, apiSecret, etherscanApiKey) in your response. - If the user pastes credentials inline in chat, suggest they use the Settings UI (render_settings tool) form instead — the form keeps secrets out of the conversation transcript. Credentials are validated against the upstream API before they're persisted; if validation fails, nothing is written. Storage: OS keychain (macOS Keychain / Linux Secret Service / Windows Credential Vault) via @napi-rs/keyring. Same path as the CLI setup flow. All five connectors use READ-ONLY credentials by design (Bybit 'Read' only, Binance 'Enable Reading' only, Etherscan is a public-data rate-limit token, Polymarket proxy wallet is already public, Solana addresses are public on-chain identifiers). Inputs (one of bybit / binance / metamask / polymarket / solana required): - connector: 'bybit' | 'binance' | 'metamask' | 'polymarket' | 'solana' - bybit: { apiKey, apiSecret, accountType: 'UNIFIED'|'CONTRACT'|'SPOT'|'FUND' (primary, also the account ID), accountTypes?: array of additional types to fan out across (e.g. ['FUND'] alongside UNIFIED so funding-wallet balances are tracked too) } - binance: { apiKey, apiSecret, includeFutures (optional bool, default false), recvWindow (optional ms) } - metamask: { address, etherscanApiKey, chainIds (number[]), trackCommonTokens (bool), hasEtherscanPro (bool) } - polymarket: { proxyWallet (0x...), sizeThreshold (default 0.01) } - solana: { address (base58), rpcUrl (optional premium RPC), dustThresholdUsd (optional, default 0.5) }

ParametersJSON Schema
NameRequiredDescriptionDefault
connectorYesWhich connector to set up. Provide the matching credential object below (e.g. connector='bybit' requires the 'bybit' object). All credentials are READ-ONLY by design.
bybitNoBybit credentials (required when connector='bybit'). Read-only API key/secret + account type.
binanceNoBinance credentials (required when connector='binance'). Read-only API key/secret.
metamaskNoMetaMask/EVM config (required when connector='metamask'). Public address + Etherscan rate-limit key + chains.
polymarketNoPolymarket config (required when connector='polymarket'). Public proxy wallet address; no secret.
solanaNoSolana config (required when connector='solana'). Public base58 address; no secret.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavior: credentials are read-only, validated against upstream API before writing, stored in OS keychain, and a 'BEHAVIOR CONTRACT' for the LLM to never echo credentials. All five connector types are explained as using read-only credentials by design.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is comprehensive but verbose, including detailed parameter lists that largely duplicate schema descriptions. The structure is clear (purpose, usage, behavior, inputs), but the length could be reduced without losing information. Still, it is well-organized and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (6 parameters, nested objects, no output schema), the description covers key aspects: validation, storage, credential nature, and a behavior contract. It lacks return value details but that is acceptable without output schema. The description is largely complete for an agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds value beyond schema by explaining the context for each connector (e.g., 'Etherscan API key — a public-data rate-limit token'), the purpose of optional parameters (e.g., accountTypes, includeFutures), and the read-only nature. This justifies a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Creates a new account by writing READ-ONLY credentials to the OS keychain' and lists specific user queries like 'add a Bybit account' or 'connect my Solana wallet'. It distinguishes from siblings (e.g., add_custom_token adds tokens, not connectors). The scope and verb+resource are explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage triggers (e.g., 'Use when the user asks: ...') and a when-not scenario: 'If the user pastes credentials inline in chat, suggest they use the Settings UI (render_settings tool) form instead.' This clearly guides when to use this tool versus alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 15 tool updatesv1.0.12
    • First observedadd_custom_token
    • First observedadd_wallet_address
    • First observedget_allocations
    • First observedget_holdings
    • First observedget_pnl
    • First observedget_polymarket_positions
    • First observedget_transactions
    • First observedlist_accounts
    • First observedlist_custom_tokens
    • First observedrefresh_data
    • First observedremove_account
    • First observedremove_custom_token
    • First observedrender_dashboard
    • First observedrender_settings
    • First observedsetup_connector

TDQS

A4.6/5.0

Scored across 15 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: account management (setup/remove/list/add_wallet), custom token CRUD (add/list/remove), data retrieval (holdings, allocations, PnL, transactions, Polymarket positions), and UI rendering (dashboard, settings). No overlapping responsibilities.

Naming Consistency5/5

All tools follow a consistent verb_noun snake_case pattern (e.g., add_custom_token, get_holdings, list_accounts, refresh_data). The prefixes are uniform (add_, get_, list_, remove_, render_, setup_, refresh_).

Tool Count5/5

The 15 tools cover a complex domain (crypto exchanges, wallets, prediction markets) without being overwhelming. Each tool serves a well-defined function, and the count is appropriate for the portfolio tracker's scope.

Completeness4/5

The tool set provides comprehensive coverage for portfolio tracking: account setup/removal, wallet management, custom tokens, data retrieval (holdings, PnL, transactions, Polymarket positions), and UI. A minor gap is the lack of a direct tool to update existing account parameters (e.g., API key rotation), though the settings UI may cover this indirectly.

Maintenance

ActivityStale
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Self-custodial crypto portfolio and DeFi MCP server. Read balances and positions (Aave, Compound, Morpho, Uniswap V3, Lido, EigenLayer) across Ethereum, Arbitrum, Polygon, and Base, and prepare transactions for approval on a Ledger via WalletConnect.
    100
    1,066 npm
    4
    Business Source 1.1
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server that provides live crypto portfolio data, token info, gas prices, swap offers, and Bitcoin balance via Zerion and Blockstream APIs.
    3
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Agent-native, self-hosted MCP server for crypto trading and DeFi management. Enables agents to query balances, execute trades, and manage positions with a policy engine and secure key storage.
    7
    Apache 2.0