MCP server
An MCP server that lets an AI coding agent — Claude Code, Claude Desktop, Cursor — build your frontend against the Solana Deads APIs using the official SDKs correctly.
It does not replace the SDKs. Everything it returns points at @solanadeads/gravemint / @solanadeads/gravemarket calls rather than raw HTTP, because an integration that speaks raw HTTP has already skipped the typed errors, the retry and backoff, the rate limiter, and the client-version header we use to see who is on what before we change anything.
Install
// claude_desktop_config.json
{
"mcpServers": {
"solanadeads": {
"command": "npx",
"args": ["-y", "@solanadeads/mcp"],
"env": { "GRAVEMINT_API_KEY": "gm_live_..." }
}
}
}claude mcp add solanadeads \
--env GRAVEMINT_API_KEY=gm_live_... \
-- npx -y @solanadeads/mcp// .cursor/mcp.json
{
"mcpServers": {
"solanadeads": {
"command": "npx",
"args": ["-y", "@solanadeads/mcp"],
"env": { "GRAVEMINT_API_KEY": "gm_live_..." }
}
}
}GraveMarket's tools need no credential. GraveMint's are invite-only — without a key the server still runs and simply exposes fewer tools, so you can try it before you have one.
It must be a gm_live_ key
An MCP server runs server-side and sends no Origin header, so a gm_pub_ or gm_test_ browser key is correctly refused — origin binding is exactly what makes those safe to ship in a browser.
The server checks the prefix at startup and tells you, rather than letting you discover it later as an opaque 403 ORIGIN_NOT_ALLOWED. See Keys and origins.
Why you want it
Some rules in the partner contract are ones a language model gets wrong from first principles — the naive version looks entirely reasonable and compiles:
| The rule | What gets written instead |
|---|---|
priceDisplay is a union, not a number | `${drop.price} SOL` — wrong for range and unknown, and leaks a price the creator deliberately hid |
| Never recompute a price | reassembles it from raw phase fields, but the cascade has ten stages and six are invisible to any public read |
| You sign, we broadcast | sendTransaction() → CLIENT_BROADCAST_NOT_ALLOWED |
Batch errors live in results[].errorCode | reads err.code, which is never set for any quantity above 1 |
walletMints counts bonus mints, eligibility does not | derives "mints remaining" from the wrong one |
review_integration checks for exactly these, with line numbers and fixes. Run it on your mint code before you ship it:
Review this file for Solana Deads integration mistakes.
Tools
Always available
| Tool | What it does |
|---|---|
sdk_reference | The real method signatures and docs, generated from the SDK source. Read this before writing integration code — it is the authoritative surface, not a summary of one. |
review_integration | The checks above, with line numbers and concrete fixes. |
GraveMarket — no credential needed
gravemarket_get_collection · gravemarket_search · gravemarket_get_collection_activity · gravemarket_get_collection_stats
GraveMint — needs GRAVEMINT_API_KEY
The dropgravemint_get_collection · gravemint_check_eligibility · gravemint_get_wallet_mints · gravemint_get_availability
Its art and historygravemint_get_gallery · gravemint_get_recently_minted · gravemint_get_all_minted · gravemint_get_traits · gravemint_get_leaderboards
Pricinggravemint_get_token_prices · gravemint_get_phase_price
Claim codes — 17.6% of live drops use them gravemint_get_claim_codes · gravemint_get_claim_code_benefits · gravemint_validate_claim_code (read-only — it does not redeem or consume a use)
Bountygravemint_get_bounty
Reads are scoped to the collections your key covers. Another drop returns 403 COLLECTION_NOT_IN_SCOPE — the same confinement the HTTP API enforces, so an agent cannot wander into someone else's data even if it tries.
What it deliberately cannot do
Two properties that are structural, not policy — they hold because of how the server is built, not because a flag is set:
- It cannot mint. There is no wallet, so it cannot sign, so
execute-mintis unreachable no matter what scopes your key carries. prepare-mintis not exposed. It is read-shaped and would be convenient for showing an agent the response, but it reserves: three-minute NFT locks and a BOGO ledger entry. A tool an agent might call speculatively must not take supply away from real collectors.sdk_referencegives you the response shape for free.
Staying in sync with the SDK
A drifted MCP is worse than none — it teaches an agent methods that do not exist, and the agent has no way to tell it was misled. So the sync is mechanical, in two layers:
- Removals are a compile error. The SDKs are real dependencies and the tool handlers call them, so a renamed or deleted method fails the package's typecheck. Casts on SDK calls are banned for this reason.
- Additions are caught by a drift gate that runs before every push: each SDK method must be reachable from a tool or explicitly listed as unexposed with a reason, and the generated reference must be current.
Nothing about the SDK surface is hand-written anywhere in the package.
