@coinpaprika/mcp 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +19 -19
- package/dist/index.js +97 -48
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the CoinPaprika MCP Server will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.3.0] - 2026-07-27
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **Intent-first tool descriptions.** Every tool description was rewritten so agents select the right tool from the description alone, each leading with what the tool returns plus `Use when asked '...'` cues (e.g. `getTickers` vs `getCoinById` vs `getContracts`). Matches the descriptions now served by the hosted worker at `mcp.coinpaprika.com`, so hosted and self-host read identically.
|
|
10
|
+
- `getCapabilities` description aligned with the hosted worker (onboarding-first wording; clarifies it describes the MCP server itself, not crypto data).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `server.json` manifest for publication to the official MCP Registry (`registry.modelcontextprotocol.io`).
|
|
15
|
+
|
|
5
16
|
## [1.1.1] - 2026-05-05
|
|
6
17
|
|
|
7
18
|
### Fixed
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npm install -g @coinpaprika/mcp
|
|
|
12
12
|
coinpaprika-mcp
|
|
13
13
|
|
|
14
14
|
# Or run directly without installation
|
|
15
|
-
npx @coinpaprika/mcp
|
|
15
|
+
npx @coinpaprika/mcp@latest
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
CoinPaprika MCP connects Claude to live crypto market data for 8,000+ coins. No API key required for free-tier features. [Installation](#installation) | [Configuration](#claude-desktop-integration) | [API Reference](https://api.coinpaprika.com)
|
|
@@ -25,7 +25,7 @@ CoinPaprika MCP connects Claude to live crypto market data for 8,000+ coins. No
|
|
|
25
25
|
- **Coin Analysis Tools**: Deep-dive into any cryptocurrency — price, team, events, exchanges, and markets
|
|
26
26
|
- **Price Trackers**: Track prices across multiple quote currencies with historical OHLCV data
|
|
27
27
|
- **Exchange Comparisons**: Compare trading pairs and volumes across 200+ exchanges
|
|
28
|
-
- **Portfolio Valuations**: Convert between currencies and track price changes over time
|
|
28
|
+
- **Portfolio Valuations**: Convert an amount between currencies ('0.5 BTC in USD') and track price changes over time
|
|
29
29
|
- **Contract Lookup**: Find tokens by their smart contract address across multiple platforms
|
|
30
30
|
|
|
31
31
|
## Installation
|
|
@@ -59,7 +59,7 @@ Add the following to your Claude Desktop configuration file:
|
|
|
59
59
|
"mcpServers": {
|
|
60
60
|
"coinpaprika": {
|
|
61
61
|
"command": "npx",
|
|
62
|
-
"args": ["@coinpaprika/mcp"]
|
|
62
|
+
"args": ["@coinpaprika/mcp@latest"]
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -72,7 +72,7 @@ Add the following to your Claude Desktop configuration file:
|
|
|
72
72
|
"mcpServers": {
|
|
73
73
|
"coinpaprika": {
|
|
74
74
|
"command": "npx",
|
|
75
|
-
"args": ["@coinpaprika/mcp"],
|
|
75
|
+
"args": ["@coinpaprika/mcp@latest"],
|
|
76
76
|
"env": {
|
|
77
77
|
"COINPAPRIKA_API_KEY": "your-api-key-here"
|
|
78
78
|
}
|
|
@@ -85,10 +85,10 @@ Add the following to your Claude Desktop configuration file:
|
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
87
|
# Add as MCP server in Claude Code
|
|
88
|
-
claude mcp add coinpaprika -- npx @coinpaprika/mcp
|
|
88
|
+
claude mcp add coinpaprika -- npx @coinpaprika/mcp@latest
|
|
89
89
|
|
|
90
90
|
# With API key
|
|
91
|
-
COINPAPRIKA_API_KEY=your-key claude mcp add coinpaprika -- npx @coinpaprika/mcp
|
|
91
|
+
COINPAPRIKA_API_KEY=your-key claude mcp add coinpaprika -- npx @coinpaprika/mcp@latest
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
### Hosted Alternative (No Installation)
|
|
@@ -122,27 +122,27 @@ If you prefer zero setup, point any MCP-compatible client directly at the hosted
|
|
|
122
122
|
|------|-------------|
|
|
123
123
|
| `getCapabilities` | Server capabilities, workflow patterns, validation rules, and best practices. **Start here.** |
|
|
124
124
|
| `status` | Server status and configuration |
|
|
125
|
-
| `getGlobal` | Global
|
|
125
|
+
| `getGlobal` | Global market overview: total market cap, 24h volume, BTC dominance ('how is the market doing') |
|
|
126
126
|
|
|
127
127
|
### Coins
|
|
128
128
|
|
|
129
129
|
| Tool | Description | Required Parameters |
|
|
130
130
|
|------|-------------|---------------------|
|
|
131
|
-
| `getCoins` | List all coins (id, name, symbol, rank) | — |
|
|
132
|
-
| `getCoinById` | Coin details (description, team, links) | `coinId` |
|
|
131
|
+
| `getCoins` | List all coins (id, name, symbol, rank); no prices, use getTickers for prices | — |
|
|
132
|
+
| `getCoinById` | Coin project details (description, team, links); no price, use getTickersById | `coinId` |
|
|
133
133
|
| `getCoinEvents` | Upcoming events for a coin | `coinId` |
|
|
134
134
|
| `getCoinExchanges` | Exchanges listing a coin | `coinId` |
|
|
135
|
-
| `getCoinMarkets` | Markets/trading
|
|
135
|
+
| `getCoinMarkets` | Markets/exchanges trading a coin ('where to buy X', 'X price on Binance') | `coinId` |
|
|
136
136
|
|
|
137
137
|
### Tickers & Prices
|
|
138
138
|
|
|
139
139
|
| Tool | Description | Required Parameters |
|
|
140
140
|
|------|-------------|---------------------|
|
|
141
|
-
| `getTickers` |
|
|
142
|
-
| `getTickersById` |
|
|
143
|
-
| `getCoinOHLCVLatest` |
|
|
144
|
-
| `getCoinOHLCVToday` |
|
|
145
|
-
| `priceConverter` | Convert between currencies | `baseCurrencyId`, `quoteCurrencyId` |
|
|
141
|
+
| `getTickers` | Current prices + market caps for top coins by market cap ('top 5 coins', market snapshot) | — |
|
|
142
|
+
| `getTickersById` | Current price, market cap & 24h change of one coin ('BTC price', 'how much is ETH') | `coinId` |
|
|
143
|
+
| `getCoinOHLCVLatest` | Latest full-day OHLC candle (not the live price) | `coinId` |
|
|
144
|
+
| `getCoinOHLCVToday` | Today's in-progress OHLC candle | `coinId` |
|
|
145
|
+
| `priceConverter` | Convert an amount between currencies ('0.5 BTC in USD') | `baseCurrencyId`, `quoteCurrencyId` |
|
|
146
146
|
|
|
147
147
|
### Exchanges
|
|
148
148
|
|
|
@@ -166,14 +166,14 @@ If you prefer zero setup, point any MCP-compatible client directly at the hosted
|
|
|
166
166
|
|------|-------------|---------------------|
|
|
167
167
|
| `getPlatforms` | List contract platforms | — |
|
|
168
168
|
| `getContracts` | Contracts on a platform | `platformId` |
|
|
169
|
-
| `getTickerByContract` |
|
|
169
|
+
| `getTickerByContract` | Token price by contract address (on-chain/DeFi tokens) | `platformId`, `contractAddress` |
|
|
170
170
|
|
|
171
171
|
### Search & Resolution
|
|
172
172
|
|
|
173
173
|
| Tool | Description | Required Parameters |
|
|
174
174
|
|------|-------------|---------------------|
|
|
175
|
-
| `search` | Search coins
|
|
176
|
-
| `resolveId` | Resolve
|
|
175
|
+
| `search` | Search coins/exchanges/people/tags by name; use FIRST to get an id | `q` |
|
|
176
|
+
| `resolveId` | Resolve a name/symbol to canonical id; call FIRST before price tools | `type`, `query` |
|
|
177
177
|
|
|
178
178
|
### Paid-Tier Tools
|
|
179
179
|
|
|
@@ -198,7 +198,7 @@ export COINPAPRIKA_API_KEY=your-api-key-here
|
|
|
198
198
|
coinpaprika-mcp
|
|
199
199
|
|
|
200
200
|
# Or pass inline
|
|
201
|
-
COINPAPRIKA_API_KEY=your-api-key-here npx @coinpaprika/mcp
|
|
201
|
+
COINPAPRIKA_API_KEY=your-api-key-here npx @coinpaprika/mcp@latest
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
Get an API key at [coinpaprika.com/api/pricing](https://coinpaprika.com/api/pricing/).
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createRequire } from 'module';
|
|
3
4
|
import fetch from 'node-fetch';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
|
|
6
|
-
// Server version
|
|
7
|
-
|
|
7
|
+
// Server version, read from package.json so the banner, capabilities
|
|
8
|
+
// document, and status tool always report the published version.
|
|
9
|
+
const SERVER_VERSION = createRequire(import.meta.url)('../package.json').version;
|
|
8
10
|
|
|
9
11
|
// Optional API key from environment
|
|
10
12
|
const API_KEY = process.env.COINPAPRIKA_API_KEY || '';
|
|
@@ -32,6 +34,21 @@ const ErrorCodes = {
|
|
|
32
34
|
// Coin ID validation regex
|
|
33
35
|
const COIN_ID_REGEX = /^[a-z0-9]+-[a-z0-9-]+$/;
|
|
34
36
|
|
|
37
|
+
// Canonical-slug guidance shared by every tool that takes a coin id. Agents
|
|
38
|
+
// kept passing ticker symbols ("BTC") or guessing slugs, then retry-looping on
|
|
39
|
+
// "resource not found" (devrel#5). The slug is NOT derivable from the symbol,
|
|
40
|
+
// so steer them to search / resolveId first.
|
|
41
|
+
const COIN_ID_DESCRIPTION =
|
|
42
|
+
"Canonical CoinPaprika coin slug in 'symbol-name' format, e.g. 'btc-bitcoin', " +
|
|
43
|
+
"'eth-ethereum', 'ada-cardano'. Do NOT pass ticker symbols ('BTC', 'AAVE') " +
|
|
44
|
+
"or guess the slug — it is not derivable from the symbol (e.g. AAVE resolves " +
|
|
45
|
+
"to 'aave-new', which you could not guess). Call search or resolveId first to " +
|
|
46
|
+
"resolve a symbol or name to its canonical id.";
|
|
47
|
+
|
|
48
|
+
// Known misspellings, surfaced in getCapabilities so MCP catalogs / tool
|
|
49
|
+
// routers / agent frameworks can match fuzzy references back to us (devrel#6).
|
|
50
|
+
const SERVER_ALIASES = ["coinparika", "coinpapika", "coinaprika", "coin-paprika", "coin paprika"];
|
|
51
|
+
|
|
35
52
|
// Structured error response builder
|
|
36
53
|
function buildErrorResponse(code, message, retryable, suggestion, correctedExample, metadata) {
|
|
37
54
|
const error = {
|
|
@@ -97,7 +114,7 @@ function parseAPIError(status, statusText, endpoint) {
|
|
|
97
114
|
ErrorCodes.CP404_NOT_FOUND,
|
|
98
115
|
'Resource not found',
|
|
99
116
|
false,
|
|
100
|
-
'
|
|
117
|
+
'No resource with that id exists — it may be misspelled, retired, or for a different entity. Call search or resolveId to get the current canonical id, then retry.',
|
|
101
118
|
undefined,
|
|
102
119
|
{ endpoint }
|
|
103
120
|
);
|
|
@@ -260,18 +277,19 @@ async function withGuidance(exec, hint) {
|
|
|
260
277
|
function buildCapabilitiesDocument() {
|
|
261
278
|
return {
|
|
262
279
|
service: "coinpaprika",
|
|
280
|
+
aliases: SERVER_ALIASES,
|
|
263
281
|
version: SERVER_VERSION,
|
|
264
|
-
description: "
|
|
282
|
+
description: "Real-time cryptocurrency prices and market data for 12,000+ coins",
|
|
265
283
|
server: {
|
|
266
284
|
name: "CoinPaprika MCP Server",
|
|
267
285
|
version: SERVER_VERSION,
|
|
268
|
-
description: "
|
|
286
|
+
description: "Real-time cryptocurrency prices, market caps, 24h changes and volumes for 12,000+ coins (Bitcoin, Ethereum, Solana, and more) and 350+ exchanges. Use for how much a crypto is worth, top coins by market cap, or live market data.",
|
|
269
287
|
documentation_url: "https://api.coinpaprika.com",
|
|
270
288
|
},
|
|
271
289
|
tools: [
|
|
272
290
|
{
|
|
273
291
|
name: "status",
|
|
274
|
-
description: "
|
|
292
|
+
description: "Get the CoinPaprika MCP server's status and configuration (uptime, mode, enabled features). Use when asked 'is the server up', 'what mode am I in', 'server status'. This reports the MCP server itself, not the crypto market; for market health use getGlobal.",
|
|
275
293
|
category: "system",
|
|
276
294
|
parameters: {},
|
|
277
295
|
returns: { type: "object" },
|
|
@@ -279,7 +297,7 @@ function buildCapabilitiesDocument() {
|
|
|
279
297
|
},
|
|
280
298
|
{
|
|
281
299
|
name: "getGlobal",
|
|
282
|
-
description: "Get global
|
|
300
|
+
description: "Get a global crypto market overview: total market cap, total 24h volume, Bitcoin dominance, and market-wide 24h change. Use for 'how is the crypto market doing', 'total market cap', 'BTC dominance'. For a single coin's price use getTickersById.",
|
|
283
301
|
category: "market",
|
|
284
302
|
parameters: {},
|
|
285
303
|
returns: { type: "object", properties: ["market_cap_usd", "volume_24h_usd", "bitcoin_dominance_percentage"] },
|
|
@@ -287,7 +305,7 @@ function buildCapabilitiesDocument() {
|
|
|
287
305
|
},
|
|
288
306
|
{
|
|
289
307
|
name: "getCoins",
|
|
290
|
-
description: "List
|
|
308
|
+
description: "List available coins (id, name, symbol, rank). Use to discover coins or map a name to an id. Does NOT include prices; for prices use getTickers or getTickersById.",
|
|
291
309
|
category: "coins",
|
|
292
310
|
parameters: { limit: { type: "integer", required: false, default: 50, min: 1, max: 250 } },
|
|
293
311
|
returns: { type: "array", items: "Coin" },
|
|
@@ -295,7 +313,7 @@ function buildCapabilitiesDocument() {
|
|
|
295
313
|
},
|
|
296
314
|
{
|
|
297
315
|
name: "getTickers",
|
|
298
|
-
description: "Get market
|
|
316
|
+
description: "Get current prices, market caps and 24h changes for the top cryptocurrencies ranked by market cap. Use for 'top 5 coins', 'top crypto today', 'biggest cryptocurrencies', or a market snapshot. For one specific coin use getTickersById.",
|
|
299
317
|
category: "market",
|
|
300
318
|
parameters: {
|
|
301
319
|
quotes: { type: "string", required: false, default: "USD", description: "Comma-separated quote currencies" },
|
|
@@ -306,7 +324,7 @@ function buildCapabilitiesDocument() {
|
|
|
306
324
|
},
|
|
307
325
|
{
|
|
308
326
|
name: "getCoinOHLCVLatest",
|
|
309
|
-
description: "Get
|
|
327
|
+
description: "Get the latest full-day OHLC candle (open/high/low/close plus volume) for a coin. For the live spot price use getTickersById.",
|
|
310
328
|
category: "coins",
|
|
311
329
|
parameters: {
|
|
312
330
|
coinId: { type: "string", required: true, format: "coin-id", example: "btc-bitcoin" },
|
|
@@ -317,7 +335,7 @@ function buildCapabilitiesDocument() {
|
|
|
317
335
|
},
|
|
318
336
|
{
|
|
319
337
|
name: "getCoinOHLCVToday",
|
|
320
|
-
description: "Get
|
|
338
|
+
description: "Get today's in-progress OHLC candle for a coin. For the current spot price use getTickersById; for past candles use getCoinOHLCVHistorical.",
|
|
321
339
|
category: "coins",
|
|
322
340
|
parameters: {
|
|
323
341
|
coinId: { type: "string", required: true, format: "coin-id" },
|
|
@@ -328,7 +346,7 @@ function buildCapabilitiesDocument() {
|
|
|
328
346
|
},
|
|
329
347
|
{
|
|
330
348
|
name: "getCoinOHLCVHistorical",
|
|
331
|
-
description: "Get
|
|
349
|
+
description: "Get OHLC candles (open/high/low/close plus volume) for a coin over a date range, for price charts, backtests and technical analysis. Use for 'daily chart for the last month', 'hourly candles since Jan 1'. For a single past date's spot value use getTickersHistoricalById; not for the current price. Requires Starter+ plan.",
|
|
332
350
|
category: "coins",
|
|
333
351
|
parameters: {
|
|
334
352
|
coinId: { type: "string", required: true, format: "coin-id" },
|
|
@@ -343,7 +361,7 @@ function buildCapabilitiesDocument() {
|
|
|
343
361
|
},
|
|
344
362
|
{
|
|
345
363
|
name: "getCoinById",
|
|
346
|
-
description: "Get
|
|
364
|
+
description: "Get descriptive project information about a coin (description, links, team, tags). Does NOT include price; for price/market cap use getTickersById.",
|
|
347
365
|
category: "coins",
|
|
348
366
|
parameters: { coinId: { type: "string", required: true, format: "coin-id", example: "btc-bitcoin" } },
|
|
349
367
|
returns: { type: "object", properties: ["id", "name", "symbol", "description", "links", "rank"] },
|
|
@@ -351,7 +369,7 @@ function buildCapabilitiesDocument() {
|
|
|
351
369
|
},
|
|
352
370
|
{
|
|
353
371
|
name: "getCoinEvents",
|
|
354
|
-
description: "Get events for a
|
|
372
|
+
description: "Get the timeline of events for a coin (launches, partnerships, hard forks, conference talks) with dates and descriptions. Use when asked 'what is happening with X', 'upcoming events for Ethereum', 'X roadmap events'. For prices or market data use getTickersById.",
|
|
355
373
|
category: "coins",
|
|
356
374
|
parameters: {
|
|
357
375
|
coinId: { type: "string", required: true, format: "coin-id" },
|
|
@@ -362,7 +380,7 @@ function buildCapabilitiesDocument() {
|
|
|
362
380
|
},
|
|
363
381
|
{
|
|
364
382
|
name: "getCoinExchanges",
|
|
365
|
-
description: "
|
|
383
|
+
description: "List the exchanges that LIST a specific coin (venue names only). Use for 'which exchanges list X', 'is X on Coinbase', 'where is X listed'. For where to BUY with per-market price and volume use getCoinMarkets; for the coin's live price use getTickersById.",
|
|
366
384
|
category: "coins",
|
|
367
385
|
parameters: {
|
|
368
386
|
coinId: { type: "string", required: true, format: "coin-id" },
|
|
@@ -373,7 +391,7 @@ function buildCapabilitiesDocument() {
|
|
|
373
391
|
},
|
|
374
392
|
{
|
|
375
393
|
name: "getCoinMarkets",
|
|
376
|
-
description: "Get markets
|
|
394
|
+
description: "Get the specific markets and trading pairs where a coin trades, with per-market price and 24h volume. Use for 'where can I buy X', 'X price on Binance', 'best market for X'. For just the list of exchange names use getCoinExchanges.",
|
|
377
395
|
category: "coins",
|
|
378
396
|
parameters: {
|
|
379
397
|
coinId: { type: "string", required: true, format: "coin-id" },
|
|
@@ -385,7 +403,7 @@ function buildCapabilitiesDocument() {
|
|
|
385
403
|
},
|
|
386
404
|
{
|
|
387
405
|
name: "getTickersById",
|
|
388
|
-
description: "Get
|
|
406
|
+
description: "Get the current price, market cap, 24h change and volume of a specific cryptocurrency in USD (or another quote currency). Use when asked 'what is the price of X', 'how much is Bitcoin worth', 'BTC to USD', 'ETH price now'. For past prices use getTickersHistoricalById.",
|
|
389
407
|
category: "market",
|
|
390
408
|
parameters: {
|
|
391
409
|
coinId: { type: "string", required: true, format: "coin-id" },
|
|
@@ -396,7 +414,7 @@ function buildCapabilitiesDocument() {
|
|
|
396
414
|
},
|
|
397
415
|
{
|
|
398
416
|
name: "getTickersHistoricalById",
|
|
399
|
-
description: "Get
|
|
417
|
+
description: "Get a coin's price and market cap at a single past date or time (a point-in-time value). Use for 'price of BTC last Tuesday', 'ETH market cap on 2024-01-01'. For OHLC chart candles over a range use getCoinOHLCVHistorical, not this. Not for the current price (use getTickersById). Requires Starter+ plan.",
|
|
400
418
|
category: "market",
|
|
401
419
|
parameters: {
|
|
402
420
|
coinId: { type: "string", required: true, format: "coin-id" },
|
|
@@ -411,7 +429,7 @@ function buildCapabilitiesDocument() {
|
|
|
411
429
|
},
|
|
412
430
|
{
|
|
413
431
|
name: "getPeopleById",
|
|
414
|
-
description: "Get information about a person in crypto
|
|
432
|
+
description: "Get profile information about a person in crypto (bio, roles, linked projects and accounts). Use when asked 'who is Vitalik Buterin', 'background on X founder', 'projects tied to this person'. Look up the person id first with search.",
|
|
415
433
|
category: "people",
|
|
416
434
|
parameters: { personId: { type: "string", required: true } },
|
|
417
435
|
returns: { type: "object" },
|
|
@@ -419,7 +437,7 @@ function buildCapabilitiesDocument() {
|
|
|
419
437
|
},
|
|
420
438
|
{
|
|
421
439
|
name: "getTags",
|
|
422
|
-
description: "
|
|
440
|
+
description: "List all CoinPaprika tags (categories such as defi, stablecoin, memecoin, ai) used to group coins. Use when asked 'what categories exist', 'list crypto sectors', or to find a tag id before calling getTagById.",
|
|
423
441
|
category: "tags",
|
|
424
442
|
parameters: {
|
|
425
443
|
additionalFields: { type: "string", required: false },
|
|
@@ -430,7 +448,7 @@ function buildCapabilitiesDocument() {
|
|
|
430
448
|
},
|
|
431
449
|
{
|
|
432
450
|
name: "getTagById",
|
|
433
|
-
description: "Get details
|
|
451
|
+
description: "Get details for a specific tag: its description and the coins and ICOs grouped under it. Use when asked 'what coins are in the defi tag', 'show the stablecoin category', 'tokens tagged X'. Get the tag id first from getTags.",
|
|
434
452
|
category: "tags",
|
|
435
453
|
parameters: {
|
|
436
454
|
tagId: { type: "string", required: true },
|
|
@@ -441,7 +459,7 @@ function buildCapabilitiesDocument() {
|
|
|
441
459
|
},
|
|
442
460
|
{
|
|
443
461
|
name: "getExchanges",
|
|
444
|
-
description: "
|
|
462
|
+
description: "List all exchanges tracked by CoinPaprika with rank, trust score and volume. Use when asked 'top exchanges', 'biggest crypto exchanges', 'list exchanges by volume', or to find an exchange id before calling getExchangeByID.",
|
|
445
463
|
category: "exchanges",
|
|
446
464
|
parameters: {
|
|
447
465
|
quotes: { type: "string", required: false },
|
|
@@ -452,7 +470,7 @@ function buildCapabilitiesDocument() {
|
|
|
452
470
|
},
|
|
453
471
|
{
|
|
454
472
|
name: "getExchangeByID",
|
|
455
|
-
description: "Get details
|
|
473
|
+
description: "Get details for a specific exchange (volume, market count, trust score, links, fiat support). Use when asked 'tell me about Binance', 'Coinbase exchange stats', 'is X a trustworthy exchange'. For the pairs traded there use getExchangeMarkets; find the exchange id first with search or getExchanges.",
|
|
456
474
|
category: "exchanges",
|
|
457
475
|
parameters: {
|
|
458
476
|
exchangeId: { type: "string", required: true },
|
|
@@ -463,7 +481,7 @@ function buildCapabilitiesDocument() {
|
|
|
463
481
|
},
|
|
464
482
|
{
|
|
465
483
|
name: "getExchangeMarkets",
|
|
466
|
-
description: "
|
|
484
|
+
description: "List the trading pairs (markets) on a specific exchange with price and 24h volume per pair. Use when asked 'what pairs trade on Binance', 'markets on Kraken', 'BTC pairs on X exchange'. For exchange-level stats use getExchangeByID.",
|
|
467
485
|
category: "exchanges",
|
|
468
486
|
parameters: {
|
|
469
487
|
exchangeId: { type: "string", required: true },
|
|
@@ -475,7 +493,7 @@ function buildCapabilitiesDocument() {
|
|
|
475
493
|
},
|
|
476
494
|
{
|
|
477
495
|
name: "getPlatforms",
|
|
478
|
-
description: "
|
|
496
|
+
description: "List the smart-contract platforms (blockchains such as ethereum, bnb, polygon) for which CoinPaprika indexes token contracts. Use when asked 'which chains are supported for contract lookups', 'list contract platforms', or to pick a platform id before calling getContracts.",
|
|
479
497
|
category: "contracts",
|
|
480
498
|
parameters: { limit: { type: "integer", required: false, default: 50 } },
|
|
481
499
|
returns: { type: "array", items: "Platform" },
|
|
@@ -483,7 +501,7 @@ function buildCapabilitiesDocument() {
|
|
|
483
501
|
},
|
|
484
502
|
{
|
|
485
503
|
name: "getContracts",
|
|
486
|
-
description: "
|
|
504
|
+
description: "List the token contract addresses tracked on a specific platform, mapping each contract to its CoinPaprika coin id. Use when asked 'contracts on ethereum', 'what tokens does X chain have', or to map a contract address to a coin id. Get the platform id first from getPlatforms; for a single contract's price use getTickerByContract.",
|
|
487
505
|
category: "contracts",
|
|
488
506
|
parameters: {
|
|
489
507
|
platformId: { type: "string", required: true },
|
|
@@ -494,7 +512,7 @@ function buildCapabilitiesDocument() {
|
|
|
494
512
|
},
|
|
495
513
|
{
|
|
496
514
|
name: "getTickerByContract",
|
|
497
|
-
description: "Get
|
|
515
|
+
description: "Get the current price and market data of a token by its contract address (for on-chain and DeFi tokens). Use when you have a 0x... or contract address rather than a name. For history use getHistoricalTickerByContract.",
|
|
498
516
|
category: "contracts",
|
|
499
517
|
parameters: {
|
|
500
518
|
platformId: { type: "string", required: true },
|
|
@@ -505,7 +523,7 @@ function buildCapabilitiesDocument() {
|
|
|
505
523
|
},
|
|
506
524
|
{
|
|
507
525
|
name: "getHistoricalTickerByContract",
|
|
508
|
-
description: "Get historical
|
|
526
|
+
description: "Get historical price and market data of a token by its contract address. Not for the current price (use getTickerByContract). Requires Starter+ plan.",
|
|
509
527
|
category: "contracts",
|
|
510
528
|
parameters: {
|
|
511
529
|
platformId: { type: "string", required: true },
|
|
@@ -521,7 +539,7 @@ function buildCapabilitiesDocument() {
|
|
|
521
539
|
},
|
|
522
540
|
{
|
|
523
541
|
name: "search",
|
|
524
|
-
description: "
|
|
542
|
+
description: "Broadly search CoinPaprika across coins, exchanges, ICOs, people and tags by name or symbol when the entity type is unknown or you are exploring. Use for 'find anything called X'. If you specifically need a coin's canonical id to fetch its price, prefer resolveId.",
|
|
525
543
|
category: "search",
|
|
526
544
|
parameters: {
|
|
527
545
|
q: { type: "string", required: true },
|
|
@@ -534,7 +552,7 @@ function buildCapabilitiesDocument() {
|
|
|
534
552
|
},
|
|
535
553
|
{
|
|
536
554
|
name: "resolveId",
|
|
537
|
-
description: "Resolve
|
|
555
|
+
description: "Resolve a coin name or symbol ('bitcoin', 'BTC', 'AAVE') to its single canonical CoinPaprika id ('btc-bitcoin', 'aave-new'). Call this FIRST whenever you have a bare symbol or name and want the coin's price, since ids are not derivable from symbols; then pass the id to getTickersById.",
|
|
538
556
|
category: "search",
|
|
539
557
|
parameters: {
|
|
540
558
|
type: { type: "string", required: true, enum: ["coin", "exchange", "people", "tags"] },
|
|
@@ -546,7 +564,7 @@ function buildCapabilitiesDocument() {
|
|
|
546
564
|
},
|
|
547
565
|
{
|
|
548
566
|
name: "priceConverter",
|
|
549
|
-
description: "Convert amount from
|
|
567
|
+
description: "Convert an amount from one crypto or fiat to another at the current rate ('how much is 0.5 BTC in USD', 'convert 100 USDC to EUR'). For a plain coin price without an amount, use getTickersById.",
|
|
550
568
|
category: "tools",
|
|
551
569
|
parameters: {
|
|
552
570
|
baseCurrencyId: { type: "string", required: true },
|
|
@@ -558,7 +576,7 @@ function buildCapabilitiesDocument() {
|
|
|
558
576
|
},
|
|
559
577
|
{
|
|
560
578
|
name: "keyInfo",
|
|
561
|
-
description: "
|
|
579
|
+
description: "Get details and usage for the current CoinPaprika API key (plan, quota, requests remaining, expiry). Use when asked 'what plan am I on', 'how many requests are left', 'is my API key valid'. Requires a Pro plan.",
|
|
562
580
|
category: "system",
|
|
563
581
|
parameters: {},
|
|
564
582
|
returns: { type: "object" },
|
|
@@ -566,7 +584,7 @@ function buildCapabilitiesDocument() {
|
|
|
566
584
|
},
|
|
567
585
|
{
|
|
568
586
|
name: "getMappings",
|
|
569
|
-
description: "Get
|
|
587
|
+
description: "Get mappings between CoinPaprika ids and external or legacy identifiers so you can reconcile ids across systems. Use when asked 'map CoinPaprika ids to X', 'id crosswalk', 'external id mapping'. Requires a Business plan.",
|
|
570
588
|
category: "system",
|
|
571
589
|
parameters: {
|
|
572
590
|
coinpaprika: { type: "string", required: false },
|
|
@@ -581,7 +599,7 @@ function buildCapabilitiesDocument() {
|
|
|
581
599
|
},
|
|
582
600
|
{
|
|
583
601
|
name: "getChangelogIDs",
|
|
584
|
-
description: "Get
|
|
602
|
+
description: "Get the ids of recent changes to CoinPaprika's coin and exchange listings (additions, renames, delistings) for syncing a local dataset. Use when asked 'what changed recently', 'new or delisted coins', 'listing changelog'. Requires a Starter+ plan.",
|
|
585
603
|
category: "system",
|
|
586
604
|
parameters: {
|
|
587
605
|
page: { type: "integer", required: false, default: 1 },
|
|
@@ -652,7 +670,7 @@ function buildCapabilitiesDocument() {
|
|
|
652
670
|
steps: [
|
|
653
671
|
"getPlatforms - List available contract platforms",
|
|
654
672
|
"getContracts - List contracts on a platform",
|
|
655
|
-
"getTickerByContract - Get
|
|
673
|
+
"getTickerByContract - Get the current price and market data of a token by its contract address (for on-chain/DeFi tokens). Use when you have a 0x.../contract address rather than a name. For history use getHistoricalTickerByContract.",
|
|
656
674
|
],
|
|
657
675
|
example_use_case: "Looking up an ERC-20 token by its Ethereum contract",
|
|
658
676
|
},
|
|
@@ -721,6 +739,34 @@ const server = new McpServer({
|
|
|
721
739
|
description: 'MCP server for accessing CoinPaprika cryptocurrency market data',
|
|
722
740
|
});
|
|
723
741
|
|
|
742
|
+
// devrel#5 parity: validate coin/currency id format BEFORE the handler runs,
|
|
743
|
+
// returning the structured CP400_INVALID_COIN_ID guidance (matches the hosted
|
|
744
|
+
// MCP). Wrapping server.tool centralizes this for every id-bearing tool
|
|
745
|
+
// (coinId / baseCurrencyId / quoteCurrencyId) instead of repeating it in each
|
|
746
|
+
// handler — so a ticker symbol like "BTC" gets actionable guidance to call
|
|
747
|
+
// search/resolveId instead of a bare upstream 404.
|
|
748
|
+
const _registerTool = server.tool.bind(server);
|
|
749
|
+
server.tool = (...regArgs) => {
|
|
750
|
+
const handler = regArgs[regArgs.length - 1];
|
|
751
|
+
const schema = regArgs.length >= 4 && regArgs[2] && typeof regArgs[2] === 'object' ? regArgs[2] : null;
|
|
752
|
+
if (typeof handler === 'function' && schema) {
|
|
753
|
+
const idFields = ['coinId', 'baseCurrencyId', 'quoteCurrencyId'].filter((f) => f in schema);
|
|
754
|
+
if (idFields.length > 0) {
|
|
755
|
+
regArgs[regArgs.length - 1] = async (args, ...rest) => {
|
|
756
|
+
for (const f of idFields) {
|
|
757
|
+
const v = args && args[f];
|
|
758
|
+
if (typeof v === 'string') {
|
|
759
|
+
const err = validateCoinId(v);
|
|
760
|
+
if (err) return formatMcpError(err);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return handler(args, ...rest);
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return _registerTool(...regArgs);
|
|
768
|
+
};
|
|
769
|
+
|
|
724
770
|
// ─── Tool 1: status ───
|
|
725
771
|
server.tool(
|
|
726
772
|
'status',
|
|
@@ -801,7 +847,7 @@ server.tool(
|
|
|
801
847
|
'getCoinOHLCVLatest',
|
|
802
848
|
'OHLCV last full day',
|
|
803
849
|
{
|
|
804
|
-
coinId: z.string().describe(
|
|
850
|
+
coinId: z.string().describe(COIN_ID_DESCRIPTION),
|
|
805
851
|
quote: z.string().optional().default('usd').describe("Quote currency (default: usd)")
|
|
806
852
|
},
|
|
807
853
|
async ({ coinId, quote = 'usd' }) => {
|
|
@@ -819,7 +865,7 @@ server.tool(
|
|
|
819
865
|
'getCoinOHLCVToday',
|
|
820
866
|
'OHLCV today',
|
|
821
867
|
{
|
|
822
|
-
coinId: z.string().describe(
|
|
868
|
+
coinId: z.string().describe(COIN_ID_DESCRIPTION),
|
|
823
869
|
quote: z.string().optional().default('usd').describe("Quote currency (default: usd)")
|
|
824
870
|
},
|
|
825
871
|
async ({ coinId, quote = 'usd' }) => {
|
|
@@ -837,7 +883,7 @@ server.tool(
|
|
|
837
883
|
'getCoinOHLCVHistorical',
|
|
838
884
|
'OHLCV historical (plan-dependent)',
|
|
839
885
|
{
|
|
840
|
-
coinId: z.string().describe(
|
|
886
|
+
coinId: z.string().describe(COIN_ID_DESCRIPTION),
|
|
841
887
|
start: z.string().describe("Start date (ISO 8601 or yyyy-mm-dd)"),
|
|
842
888
|
end: z.string().optional().describe("End date (ISO 8601 or yyyy-mm-dd)"),
|
|
843
889
|
limit: z.number().optional().default(50).describe("Number of data points (default: 50, max: 250)"),
|
|
@@ -858,7 +904,7 @@ server.tool(
|
|
|
858
904
|
server.tool(
|
|
859
905
|
'getCoinById',
|
|
860
906
|
'Coin details (descriptive info)',
|
|
861
|
-
{ coinId: z.string().describe(
|
|
907
|
+
{ coinId: z.string().describe(COIN_ID_DESCRIPTION) },
|
|
862
908
|
async ({ coinId }) => {
|
|
863
909
|
try {
|
|
864
910
|
const response = await fetchFromAPI(`/coins/${encodeURIComponent(coinId)}`);
|
|
@@ -874,7 +920,7 @@ server.tool(
|
|
|
874
920
|
'getCoinEvents',
|
|
875
921
|
'Events for a coin',
|
|
876
922
|
{
|
|
877
|
-
coinId: z.string().describe(
|
|
923
|
+
coinId: z.string().describe(COIN_ID_DESCRIPTION),
|
|
878
924
|
limit: z.number().optional().default(50).describe("Number of events to return (default: 50, max: 250)")
|
|
879
925
|
},
|
|
880
926
|
async ({ coinId, limit = 50 }) => {
|
|
@@ -895,7 +941,7 @@ server.tool(
|
|
|
895
941
|
'getCoinExchanges',
|
|
896
942
|
'Exchanges for a coin',
|
|
897
943
|
{
|
|
898
|
-
coinId: z.string().describe(
|
|
944
|
+
coinId: z.string().describe(COIN_ID_DESCRIPTION),
|
|
899
945
|
limit: z.number().optional().default(50).describe("Number of exchanges to return (default: 50, max: 250)")
|
|
900
946
|
},
|
|
901
947
|
async ({ coinId, limit = 50 }) => {
|
|
@@ -916,7 +962,7 @@ server.tool(
|
|
|
916
962
|
'getCoinMarkets',
|
|
917
963
|
'Markets for a coin',
|
|
918
964
|
{
|
|
919
|
-
coinId: z.string().describe(
|
|
965
|
+
coinId: z.string().describe(COIN_ID_DESCRIPTION),
|
|
920
966
|
quotes: z.string().optional().describe("Comma-separated quote currencies"),
|
|
921
967
|
limit: z.number().optional().default(50).describe("Number of markets to return (default: 50, max: 250)")
|
|
922
968
|
},
|
|
@@ -939,7 +985,7 @@ server.tool(
|
|
|
939
985
|
'getTickersById',
|
|
940
986
|
'Ticker for a specific coin',
|
|
941
987
|
{
|
|
942
|
-
coinId: z.string().describe(
|
|
988
|
+
coinId: z.string().describe(COIN_ID_DESCRIPTION),
|
|
943
989
|
quotes: z.string().optional().describe("Comma-separated quote currencies")
|
|
944
990
|
},
|
|
945
991
|
async ({ coinId, quotes }) => {
|
|
@@ -958,7 +1004,7 @@ server.tool(
|
|
|
958
1004
|
'getTickersHistoricalById',
|
|
959
1005
|
'Historical ticks for a coin (plan dependent)',
|
|
960
1006
|
{
|
|
961
|
-
coinId: z.string().describe(
|
|
1007
|
+
coinId: z.string().describe(COIN_ID_DESCRIPTION),
|
|
962
1008
|
start: z.string().describe("Start date (ISO 8601 or yyyy-mm-dd)"),
|
|
963
1009
|
end: z.string().optional().describe("End date"),
|
|
964
1010
|
limit: z.number().optional().default(50).describe("Number of data points (default: 50, max: 250)"),
|
|
@@ -1228,8 +1274,8 @@ server.tool(
|
|
|
1228
1274
|
'priceConverter',
|
|
1229
1275
|
'Convert base -> quote',
|
|
1230
1276
|
{
|
|
1231
|
-
baseCurrencyId: z.string().describe("
|
|
1232
|
-
quoteCurrencyId: z.string().describe("
|
|
1277
|
+
baseCurrencyId: z.string().describe("Required. Source CoinPaprika coin slug (e.g., 'btc-bitcoin'). Full slugs only; ticker symbols like 'BTC' are rejected upstream. Call search or resolveId first."),
|
|
1278
|
+
quoteCurrencyId: z.string().describe("Required. Target CoinPaprika coin slug (e.g., 'usd-us-dollars', 'eth-ethereum'). Full slugs only; ticker symbols like 'USD' are rejected upstream."),
|
|
1233
1279
|
amount: z.number().optional().default(0).describe("Amount to convert (default: 0)")
|
|
1234
1280
|
},
|
|
1235
1281
|
async ({ baseCurrencyId, quoteCurrencyId, amount = 0 }) => {
|
|
@@ -1280,7 +1326,10 @@ server.tool(
|
|
|
1280
1326
|
},
|
|
1281
1327
|
async (params) => {
|
|
1282
1328
|
const qs = new URLSearchParams();
|
|
1283
|
-
|
|
1329
|
+
// Allowlist the upstream-recognized mapping params only — never forward
|
|
1330
|
+
// MCP-only or unexpected fields, which the upstream rejects with a 400.
|
|
1331
|
+
for (const k of ['coinpaprika', 'coinmarketcap', 'coingecko', 'cryptocompare', 'isin', 'dti']) {
|
|
1332
|
+
const v = params[k];
|
|
1284
1333
|
if (v) qs.set(k, String(v));
|
|
1285
1334
|
}
|
|
1286
1335
|
return withGuidance(
|
|
@@ -1309,7 +1358,7 @@ server.tool(
|
|
|
1309
1358
|
// ─── Tool 30: getCapabilities ───
|
|
1310
1359
|
server.tool(
|
|
1311
1360
|
'getCapabilities',
|
|
1312
|
-
|
|
1361
|
+
"Get the server's capabilities, supported workflow patterns, validation rules and recommended tool sequences. Use FIRST to onboard as an agent, or when asked 'what can this server do', 'how should I chain these tools', 'what are the parameter rules'. This describes the MCP server itself, not crypto data.",
|
|
1313
1362
|
{},
|
|
1314
1363
|
async () => {
|
|
1315
1364
|
try {
|
package/package.json
CHANGED