@cyanheads/coingecko-mcp-server
Market data for 15,000+ cryptocurrencies — prices, history, trends, and deep coin metadata via CoinGecko.
8 Tools • 2 Resources • 1 Prompt
Overview
Cryptocurrency market data from CoinGecko — prices, rankings, historical charts, and deep coin profiles across 15,000+ coins. Resolve a name or ticker to CoinGecko's slug-keyed ID, then chain into price lookups, ranked markets, and trend charts. Runs as a stdio process or a local Streamable HTTP server.
| Tool | Description |
|---|
coingecko_search_coins | Resolve a coin name or ticker symbol to a CoinGecko ID (slug). The required first step before any ID-keyed tool. |
coingecko_get_prices | Current price and core market stats for one or more coins in one or more currencies. Batch up to 250 slugs; reports unresolved ids in a missing field. |
coingecko_list_markets | Ranked market table — top coins by market cap, volume, or 24h change, optionally filtered to a category. |
coingecko_get_coin | Deep single-coin profile — description, links, market data, developer activity, community, sentiment. sections trims the large record. |
coingecko_get_market_chart | Historical price, market cap, and volume series. recent (last N days, auto-granularity) or range (explicit Unix-second window) mode. |
coingecko_get_trending | Coins trending on CoinGecko in the last 24 hours, by search volume. |
coingecko_get_global | Global crypto market snapshot — total market cap and volume, BTC/ETH dominance, active counts, ongoing ICOs, 24h change. |
coingecko_list_categories | Coin categories (category_id + display name) — the valid slugs for coingecko_list_markets's category filter. |
Resources
| Resource | Description |
|---|
coingecko://coin/{id} | Deep coin record by slug — same data as coingecko_get_coin (full record, USD market figures). |
coingecko://global | Global crypto market snapshot — same data as coingecko_get_global. |
All resource data is also reachable via tools — the resources mirror coingecko_get_coin and coingecko_get_global exactly, so tool-only clients lose nothing. Large collections (markets, categories) are not exposed as resources; use coingecko_list_markets and coingecko_list_categories instead.
Prompts
| Prompt | Description |
|---|
coingecko_coin_research | Guides a full single-coin research pass through the search → get_coin → get_market_chart → get_global chain. |
Capability reference
- Tickers are not unique (many coins share
ETH/USDC) — returns ranked candidates with id, symbol, name, and marketCapRank to disambiguate
- Returns the top 25 matches by relevance;
enrichment.truncated plus shown/cap disclose when the list is capped
- An empty result is a normal outcome, not an error —
enrichment.notice suggests broadening the query
- Batch up to 250 slugs across one or more currencies (
vs_currencies defaults to ["usd"]) in a single call
- One row per
(id, currency) that returned a price; market cap, 24h volume, and 24h change are optional per row
- A wrong or unknown slug returns a silent empty response upstream rather than an error —
missing lists unresolved ids so a typo reads as "unresolved," not "nonexistent"
- Unsupported currency codes are silently dropped upstream and surfaced via
enrichment.notice
lastUpdatedAtUnixSec, when present, is Unix seconds — coingecko_get_market_chart's point timestamps are milliseconds
- Throws
all_missing only when no requested id resolves
- Sort by market cap, volume, or 24h price change, ascending or descending, via
order
- Optional
category filter — pass a categoryId from coingecko_list_categories
- Pagination via
page and per_page (default 50, max 250 rows); enrichment.truncated discloses when a page fills
- Per-coin fields include price, cap, volume, 1h/24h/7d change, supply, and ATH/ATL
- Throws
unknown_category when a category filter returns empty — CoinGecko returns empty rather than erroring on an unrecognized slug
- Six sections —
profile, market, links, developer, community, sentiment; sections trims the response to just what's needed (the full record is large)
- Market figures are denominated in
vs_currency (defaults to usd)
profile.categories are display names (e.g. "Layer 1 (L1)") — distinct from the categoryId slugs coingecko_list_categories returns
- Sparse upstream fields are omitted rather than fabricated
- Throws
coin_not_found when the slug isn't recognized upstream
recent mode: last N days (or "max"); granularity auto-scales by span — ≤1 day → ~5-min, 2–90 → hourly, >90 → daily
range mode: an explicit window via from/to as Unix seconds
- Returns timestamped point arrays for price, market cap, and volume in
vs_currency (defaults to usd)
- Point timestamps are Unix milliseconds —
coingecko_get_prices's lastUpdatedAtUnixSec is seconds
- Throws
invalid_range on a mode/parameter mismatch, coin_not_found when the slug isn't recognized
- No parameters — 24-hour trending coins by search volume, each with
marketCapRank, priceUsd, priceBtc, and priceChangePercentage24hUsd when available
- Upstream reports market cap and volume as pre-formatted display strings, not numbers — those fields are omitted rather than presented as numeric data
nftCount reports the trending-NFT count upstream also returns; NFT detail itself isn't surfaced
- No coin id needed — one call for the macro snapshot
vs_currency (default usd) scopes only total market cap and volume; dominance and the 24h change percentages are always USD-denominated upstream
- Throws
unsupported_currency when the requested currency has no entry in upstream's per-currency maps — /global has no upstream currency validation, so an unknown code silently returns no totals
- Returns active-cryptocurrency and active-market counts, ongoing ICO count, and a Unix-second snapshot timestamp
- Optional
name_contains filters the ~800-category list locally by whitespace-separated tokens — case- and punctuation-insensitive, every token must appear in the name
enrichment.totalCount reports the total category count before any local filter
- Returned
categoryId values are what coingecko_list_markets's category param expects
- A filter that matches nothing returns
enrichment.notice suggesting an unfiltered browse instead
coingecko://coin/{id} resource
- Same data as
coingecko_get_coin's full record, always denominated in USD
{id} is a CoinGecko slug (e.g. "bitcoin") — resolve tickers with coingecko_search_coins
- Throws
coin_not_found when the slug isn't recognized upstream
coingecko://global resource
- Same data as
coingecko_get_global, always denominated in USD
- No parameters
coingecko_coin_research prompt
- Argument:
coin required — a name, ticker, or CoinGecko slug
- Returns an assistant framing message plus a user message that walks the search → get_coin → get_market_chart → get_global chain
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
CoinGecko-specific:
- Type-safe client for the CoinGecko v3 REST API, with one service method per endpoint
- Runs keyless on the public tier; an optional Demo API key (
x-cg-demo-api-key) raises the rate ceiling with no other config change
- Calibrated retry/backoff on the rate-limited public tier — 429s, 5xx, and timeouts retry;
Retry-After is honored
- Three distinct upstream error shapes mapped to typed reasons (404 →
coin_not_found, 429 → rate_limited, the /simple/price silent miss handled as a missing field, not an error)
Agent-friendly output:
- Search-before-query enforced through descriptions — every ID-keyed tool states slugs-not-tickers and points back to
coingecko_search_coins
- Provenance and graceful degradation —
coingecko_get_prices reports unresolved ids and dropped currencies instead of failing; truncation and applied filters are disclosed via enrichment
- Units made explicit — timestamp fields document seconds vs. milliseconds so callers don't misread them
- Required CoinGecko attribution ships on every tool and resource output; the research prompt instructs the agent to attribute its findings the same way
Getting started
Add the following to your MCP client configuration file. No API key is required — set COINGECKO_API_KEY only to attach the higher CoinGecko Demo tier (see Configuration).
{
"mcpServers": {
"coingecko-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/coingecko-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"coingecko-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/coingecko-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"coingecko-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/coingecko-mcp-server:latest"
]
}
}
}
To attach a CoinGecko Demo key, add "COINGECKO_API_KEY": "your-demo-key" to the env block (or -e COINGECKO_API_KEY=your-demo-key for Docker).
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- No account or API key required to run. Optionally, a free CoinGecko Demo API key for a higher rate ceiling (10k calls/month, 100 req/min) over the shared keyless public pool.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/coingecko-mcp-server.git
- Navigate into the directory:
- Install dependencies:
- Configure environment (optional):
Configuration
The server runs with zero configuration. The one server-specific variable is optional:
| Variable | Description | Default |
|---|
COINGECKO_API_KEY | Optional CoinGecko Demo API key. When set, it is sent as the x-cg-demo-api-key header for the dedicated Demo tier (10k/mo, 100 req/min); the base URL is unchanged. Absent → keyless public tier (shared, IP-throttled). | — |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for the HTTP server. | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path. | /mcp |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto (resolves to stateful). The server declares stateless in code; an explicit value overrides it, while an empty or unsubstituted ${…} value falls through to stateless. | stateless |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry instrumentation (spans, metrics, completion logs). | false |
See .env.example for the full list of optional overrides.
Rate limits and freshness. The keyless public tier shares an IP-throttled pool, so bursty multi-tool workflows can hit 429s under light load; the server backs off and retries, but cannot raise the ceiling — a free Demo key does. CoinGecko data refreshes roughly every 60 seconds and is not tick-level.
Running the server
Local development
-
Build and run:
bun run rebuild
bun run start:stdio
bun run start:http
-
Run checks and tests:
bun run devcheck
bun run test
bun run lint:mcp
Docker
docker build -t coingecko-mcp-server .
docker run --rm -e MCP_TRANSPORT_TYPE=http -p 3010:3010 coingecko-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/coingecko-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
| Directory | Purpose |
|---|
src/index.ts | createApp() entry point — registers tools/resources/prompts and inits the CoinGecko service. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). |
src/mcp-server/resources | Resource definitions (*.resource.ts). |
src/mcp-server/prompts | Prompt definitions (*.prompt.ts). |
src/services/coingecko | CoinGecko v3 REST API service — HTTP client, retry/error mapping, response normalization, domain types. |
tests/ | Unit and integration tests mirroring src/. |
Development guide
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catch in tool logic
- Use
ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
- Register new tools and resources via the
createApp() arrays in src/index.ts
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.