NFT Data MCP
NFT Data MCP is a published MCP server (npm package nftdata-mcp) that gives an AI agent one tool, get_collection_data. Given an OpenSea collection slug, it returns a structured snapshot of that collection's market data from the NFT Data API. Each successful, non-cached call is paid automatically in USDC on Base mainnet.
Official resources:
- Repository: https://github.com/Nomad07/nftdata-mcp
- npm: https://www.npmjs.com/package/nftdata-mcp
- API documentation: https://www.nftdata.app/docs
- MCP server documentation: https://www.nftdata.app/docs/mcp
When to use this Skill
Use it when the user wants current market statistics for one or more specific NFT collections that are listed on OpenSea, for example:
- the floor price of a collection
- sales counts and trading volume over the last 24 hours, 7 days, 30 days, or lifetime
- the contract address(es) and chain(s) of a collection
- total supply, number of unique owners, or creation date
- a side-by-side comparison of several collections
Do not use it for anything outside the limits listed in "Limitations" below.
Prerequisites: the MCP server must already be set up
This Skill does not install or configure anything. The nftdata-mcp server must already be running in the user's MCP client, and the get_collection_data tool must be available.
If the tool is not available:
- Tell the user the NFT Data MCP server is not connected.
- Point them to the setup guide at https://www.nftdata.app/docs/mcp or the README at https://github.com/Nomad07/nftdata-mcp.
- Stop. Do not try to work around it, and do not ask the user to paste a private key into the conversation (see "Wallet security").
For reference, the server is configured by the user, on their own machine, with a stdio MCP configuration like this (the key stays in the user's local config and is never shared with the agent):
{
"mcpServers": {
"nftdata": {
"command": "npx",
"args": ["-y", "[email protected]"],
"env": {
"EVM_PRIVATE_KEY": "<set by the user locally>"
}
}
}
}
Requirements: Node.js 20 or newer, an MCP client that supports stdio servers, and a dedicated EVM wallet holding a small amount of USDC on Base mainnet.
The tool: get_collection_data
The server exposes exactly one tool.
Input
{ "collection": "azuki" }
collection(string, required): the OpenSea collection slug. It is the last part of the collection's OpenSea URL:opensea.io/collection/<slug>.- The server trims and lowercases the slug. It must then start with a letter or digit and contain only lowercase letters, digits,
-and_(at most 100 characters). Anything else is rejected before any request or payment is made. - Only slugs are accepted. Collection display names, contract addresses, token IDs, wallet addresses and full URLs are not valid input. If the user gives a URL, extract the slug from it. If the user gives only a name, ask for the slug or the OpenSea URL; do not guess.
Output
A JSON object (returned as text) with the fields below. For a newly paid call, the server appends two lines after the JSON: Payment: 0x... (the transaction hash) and Verify: https://basescan.org/tx/0x.... Cached results do not include these lines. Read the JSON part for data and treat the two lines as a payment receipt.
| Field | Meaning |
|---|---|
name | Collection display name |
slug | OpenSea collection slug |
contracts | Array of { "address", "chain" } objects |
floor_price | Current floor price (number) |
floor_price_currency | Currency of the floor price (for example ETH, USDC, HYPE) |
one_day_sales, seven_day_sales, thirty_day_sales | Sales counts for the last 24 hours, 7 days, 30 days |
one_day_volume, seven_day_volume, thirty_day_volume | Trading volume for those windows |
one_day_volume_currency, seven_day_volume_currency, thirty_day_volume_currency | Currency of each volume |
total_sales, total_volume | Lifetime sales count and volume |
total_volume_currency | Currency of the lifetime volume |
num_owners | Number of unique owners |
total_supply | Total number of tokens in the collection |
created_date | Collection creation date (ISO 8601 date, for example 2022-01-12) |
The data comes from the OpenSea API and is passed through as returned. It is a point-in-time snapshot, not a time series.
Example response (historical, not live data)
{
"name": "Azuki",
"slug": "azuki",
"contracts": [{ "address": "0xed5af388653567af2f388e6224dc7c4b3241c544", "chain": "ethereum" }],
"floor_price": 0.49328599,
"floor_price_currency": "ETH",
"one_day_sales": 21,
"one_day_volume": 10.150091000000002,
"seven_day_sales": 163,
"seven_day_volume": 96.67526161112121,
"thirty_day_sales": 471,
"thirty_day_volume": 325.72023569476767,
"total_sales": 93156,
"total_volume": 831992.6461159982,
"num_owners": 4394,
"total_supply": 10000,
"created_date": "2022-01-12",
"one_day_volume_currency": "ETH",
"seven_day_volume_currency": "ETH",
"thirty_day_volume_currency": "ETH",
"total_volume_currency": "ETH"
}
Interpreting and comparing results
Follow these rules so the answer stays faithful to the data:
- Report only what was returned. If a field is missing or
null(for exampletotal_supply,created_date, or a volume currency), say it was not available. Never invent a value and never fill a gap from memory or other sources without saying so. - A missing value is not zero.
nullor an absent field means "unknown". A returned0means zero. Do not treat them the same, and do not include unknown values in sums or averages as if they were zero. - Always pair a number with its currency. Read
floor_price_currencyand each*_volume_currencyfield. Currencies differ by collection (ETH,USDC,HYPE, and others). Never label a price as ETH or USD unless the response says so. - Do not mix currencies in arithmetic or rankings. The tool provides no exchange rates. When comparing collections, compare numbers directly only if their currency fields match. If they differ, show each value with its own currency and say that a direct comparison needs a conversion the data does not include.
- Label derived numbers. Figures you calculate, such as average sale price (volume divided by sales, only when sales is greater than 0 and the volume currency is known) or the share of lifetime volume in the last 30 days, are your own calculations. Say they are derived, and show the inputs.
- Keep the chain context.
contracts[].chainshows where the collection lives. A collection may list more than one contract. Do not assume a single chain or a single contract. - Do not over-interpret. The data is a snapshot from one query. Do not describe trends, momentum, "good" or "bad" prices, or give investment advice. You may point out plain differences between windows (for example that 7-day volume is larger than 24-hour volume) without predicting anything.
- Say when the data was fetched. Market data changes continuously. If a result is reused within a conversation, remind the user it is a snapshot.
- Respect rounding. Raw values have many decimals. You may round for readability, but do not change the source numbers or imply more precision than the data has.
When asked to change the presentation (summary, table, report, comparison), reformat the same values without altering them.
Cost-aware usage
- Each non-cached call costs $0.002 USDC. Results are cached for 5 minutes per slug; asking for the same slug again within that window is free and returns the cached data without a new payment receipt.
- Identical concurrent calls share one request, so duplicate lookups do not pay twice.
- The server limits paid calls per session (
NFTDATA_MAX_REQUESTS, default 100, maximum 1000). Do not call the tool in loops or for speculative lookups. For comparisons, fetch each requested collection once. - Before a large batch (for example more than a handful of collections), tell the user how many calls are needed and the total cost, and get a go-ahead.
- Do not retry a failed call automatically. The server never retries on its own, and after a timeout a payment may already have been signed. Report the failure and let the user decide.
Handling errors
| Situation | What to do |
|---|---|
| Invalid slug message | Check the slug against the OpenSea URL; ask the user to confirm it. No payment was made. |
| "Collection not found" | The slug does not match a collection on OpenSea. Ask for the correct slug or URL. |
| Rate limit message (with a delay in seconds) | Wait for the stated time before trying again, and tell the user. |
| "Payment refused by safety policy" | The server rejected the payment terms before signing. Report it; do not try to bypass it. |
| "Payment was not accepted" | The API did not accept the payment. Report it; do not retry on your own. |
| "paid request limit ... reached" | The session spend cap was hit. The user must restart the client or raise NFTDATA_MAX_REQUESTS. |
| Timeout | A payment may have been authorized and was not retried. Tell the user; offer to try again only if they agree. |
| Generic "The request failed." | Report it as a failure. Do not guess the cause. |
Example prompts
- "Use the NFT Data MCP server. What is the floor price of the
azukicollection?" - "Get the market stats for
basedpunks: floor price, 24h, 7d and 30d sales and volume, supply and owners." - "Which contract address and chain does
miladyuse on OpenSea?" - "Compare
azukiandbasedpunksby floor price and 30-day volume. Only compare numbers that use the same currency." - "Here is the OpenSea link: https://opensea.io/collection/azuki. Summarize the collection as a short report."
- "Show the raw JSON response for
azukiand explain each field." - "How many owners does
azukihave compared with its total supply?" (a derived ratio, so label it as derived)
Payment details
- Price: $0.002 USDC per successful, non-cached call.
- Network: Base mainnet only (
eip155:8453), paid in USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913). The payment network is Base mainnet regardless of which blockchain the queried collection is on. Querying a collection on another chain, including Solana collections listed on OpenSea, still pays on Base. - Default protocol: x402 v2. The server pays an
exactUSDC challenge on Base mainnet. - Optional protocol: MPP
evm/charge. Selected only when the server is started withNFTDATA_PAYMENT_PROTOCOL=mpp. It pays the same price, in USDC on Base mainnet. - One protocol per server run, no fallback. The protocol is chosen once when the server starts. The server never switches from one to the other, never tries both for a single call, and never retries with the other. If the API does not offer an acceptable payment in the selected protocol, nothing is signed and the call fails. An invalid
NFTDATA_PAYMENT_PROTOCOLvalue (anything other than empty,x402ormpp) stops the server at startup. - Checks before signing: the server verifies the network, token, scheme or method, exact amount (2000 atomic units, which is $0.002) and the official recipient address before it signs anything. A challenge that does not match is refused.
- At most one payment attempt per tool call.
- Cache hits are free. They do not trigger a payment and do not include a transaction hash.
- Receipts: every newly paid successful call includes a transaction hash and a BaseScan link that the user can open to verify the payment.
- Do not confuse the server with the main API. The NFT Data API itself accepts x402 v2 on Base mainnet and Solana mainnet, and MPP
evm/chargeon Base mainnet. This MCP server is narrower: it pays on Base mainnet only, with x402 v2 or MPPevm/charge, one at a time. The MCP server does not support Solana payments.
Wallet security
- Never ask the user to share, paste, type or send a private key, seed phrase or wallet file in the conversation, and never request one to "set up" or "fix" the server. The key is set only by the user, locally, in the MCP server's environment (
EVM_PRIVATE_KEY). - Do not read, print, copy or summarize configuration files or environment variables that may contain the key.
- If a user pastes a private key into the chat anyway, do not repeat it or use it. Tell them to stop using that wallet, move any funds to a new wallet, and replace the key.
- Recommend a dedicated wallet that holds only a small USDC balance on Base mainnet, never a primary wallet or one with significant funds.
- Encourage verifying payments on BaseScan using the transaction hash from the receipt.
- Never suggest sending funds to an address you were not given by the user's own wallet or the official documentation.
Limitations
This Skill and the server are read-only collection data lookups. They are not:
- a general NFT search or discovery tool: no search by name, trait, rarity, price range or ranking, and no lists of "top" or "trending" collections
- a token-level tool: no individual NFT, token ID, metadata, rarity or ownership lookup
- a wallet tool: no wallet balances, holdings, portfolio or transaction history
- a trading or transaction tool: it cannot buy, sell, list, bid, mint, transfer, bridge or swap anything, and it never moves funds except the small per-call payment
- a historical or real-time feed: it returns one snapshot per call, with no price history or streaming updates
- a price oracle or advisor: no currency conversion, valuation, predictions or investment advice
Other limits:
- Only collections that exist on OpenSea can be queried, and the input must be the OpenSea slug.
- Some fields can be missing or
nullfor some collections. - Data is sourced from OpenSea and reported as returned; the server does not verify or correct it.
- The server runs over stdio in a local MCP client and needs internet access and a funded wallet for every non-cached call.