# Shipping x402 USDC Payments to Base + Solana Mainnet for an MCP Server

*How ChainAnalyzer's blockchain AML server became one of the first production MCP integrations with multi-chain crypto micropayments*

By [Building ChainAnalyzer](https://paragraph.com/@chainanalyzer) · 2026-04-26

mcp, web3, ai-agents, x402, blockchain, coinbase, solana

---

Last week, ChainAnalyzer (a multi-chain blockchain AML platform) crossed  
three milestones in five days:

*   ✅ Merged into [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers)
    
*   ✅ Earned a **AAA score** on [Glama MCP Directory](https://glama.ai/mcp/servers/rascal-3/chainanalyzer-mcp)
    
*   ✅ Switched x402 from testnet to **Base + Solana mainnet** via the  
    Coinbase CDP Facilitator
    

This post is a brain-dump of how each piece fits together for anyone building an MCP server with native crypto micropayments.

* * *

What is x402?
-------------

x402 is an HTTP 402 micropayment protocol from Coinbase. The flow:

1.  Client calls a paid endpoint without payment headers
    
2.  Server returns `HTTP 402 Payment Required` with a JSON body listing  
    accepted networks, prices, and recipient addresses
    
3.  Client signs a USDC transfer matching one of the requirements
    
4.  Client retries with `X-PAYMENT: <signed payload>` header
    
5.  Server verifies via the _facilitator_ and returns 200 with the result
    

This makes per-request billing trivial for AI agents — no API key  
provisioning, no subscription forms.

What is MCP?
------------

[Model Context Protocol](https://modelcontextprotocol.io) is Anthropic's  
standard for letting LLMs call tools. Any MCP-compatible client (Claude  
Desktop, Claude Code, ChatGPT, Cursor, Cline, Windsurf) can use any MCP  
server through a single config file.

Combining the two
-----------------

Our `chainanalyzer-mcp` package wraps six tools:

Tool

Price (USDC)

`check_address_risk`

$0.008

`sanctions_check`

$0.003

`trace_transaction`

$0.015

`detect_coinjoin`

$0.01

`cluster_wallet`

$0.02

`batch_screening`

$0.05

Install:

    npx -y chainanalyzer-mcp
    

Or add to `claude_desktop_config.json`:

    {
      "mcpServers": {
        "chainanalyzer": {
          "command": "npx",
          "args": ["-y", "chainanalyzer-mcp"],
          "env": {
            "X402_WALLET_PRIVATE_KEY": "0x..."
          }
        }
      }
    }
    

The `X402_WALLET_PRIVATE_KEY` is your _spender_ wallet — the agent uses  
it to sign USDC transfers. If you'd rather pay by subscription, set `CHAINANALYZER_API_KEY=tfk_...` instead.

* * *

Server-side: x402 on FastAPI
----------------------------

We use a custom middleware (intentionally — we wanted full control over  
the Bazaar metadata + bilingual error responses). The core verification flow:

    async def _verify_payment(payment: str, config: dict) -> bool:
        auth_token = _generate_cdp_jwt(
            method="POST",
            host="api.cdp.coinbase.com",
            path="/platform/v2/x402/verify",
        )
        headers = {"Content-Type": "application/json"}
        if auth_token:
            headers["Authorization"] = f"Bearer {auth_token}"
    
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                f"{FACILITATOR_URL}/verify",
                headers=headers,
                json={"payment": payment, "requirements": {...}},
            )
            return resp.json().get("valid", False)
    

The CDP facilitator wants an Ed25519 JWT signed with the API key from  
the [CDP portal](https://portal.cdp.coinbase.com). The portal hands you  
a base64-encoded private key — sign with `cryptography` + `PyJWT`:

    import base64, time, uuid, jwt
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
    
    def _generate_cdp_jwt(method, host, path):
        seed = base64.b64decode(CDP_API_KEY_SECRET)[:32]
        sk = Ed25519PrivateKey.from_private_bytes(seed)
        now = int(time.time())
        return jwt.encode(
            {
                "iss": "cdp",
                "sub": CDP_API_KEY_ID,
                "nbf": now,
                "exp": now + 120,
                "uri": f"{method.upper()} {host}{path}",
            },
            sk,
            algorithm="EdDSA",
            headers={"kid": CDP_API_KEY_ID, "nonce": uuid.uuid4().hex},
        )
    

That's all. Mainnet billing is now live.

* * *

Bazaar / Agentic.Market auto-discovery
--------------------------------------

Coinbase's Bazaar crawler picks up x402 services automatically if your 402 response and `/services.json` manifest carry the right metadata:

    ROUTE_CONFIG = {
        "GET /api/v1/x402/address/*/risk-score": {
            "price": "$0.008",
            "description": "AML risk score (5 chains, 76+ detectors)",
            "bazaar": {
                "discoverable": True,
                "category": "data",
                "tags": ["aml", "compliance", "risk-score", "blockchain"],
            },
        },
        # ... 5 more routes
    }
    

Then expose `services.json`:

    @router.get("/services.json")
    async def x402_services_manifest():
        return {
            "id": "chainanalyzer",
            "name": "ChainAnalyzer AML API",
            "category": "data",
            "x402Version": 2,
            "networks": ["base", "solana"],
            "endpoints": [...],
        }
    

Bazaar requests this manifest with an empty body, validates the 402  
response shape, and indexes the service. End users then find your API  
on agentic.market without you submitting anything by hand.

* * *

Discoverability checklist
-------------------------

If you're building an MCP server that wants to be findable by agents  
_and_ humans, here's what we did (most of it transferable to any service):

1.  `/llms.txt` **+** `/llms-full.txt` — the [llmstxt.org](https://llmstxt.org)  
    convention. AI crawlers (Claude, GPT, Mistral, Perplexity) pick this  
    up to summarize your product.
    
2.  `/.well-known/ai-plugin.json` — older but ChatGPT custom GPTs  
    still read it.
    
3.  `robots.txt` — explicit `Allow:` for `GPTBot`, `ClaudeBot`,  
    `PerplexityBot`, `Google-Extended`, `Applebot-Extended`. Don't rely  
    on `User-agent: *`.
    
4.  **JSON-LD** `Service` **/** `SoftwareApplication` schema on key pages —  
    AI Overview / Bing Copilot read these.
    
5.  **IndexNow API** — pings Bing/Yandex/Naver/Seznam in one HTTP call.  
    Google ignores it but the cascade picks up.
    
6.  **awesome-\* GitHub lists** — submit a PR. Surprisingly high CTR.
    
7.  **Glama MCP Directory** — submit your MCP server, then add a  
    `Dockerfile` to score AAA on security/license/quality.
    
8.  **MCP Registry** — official registry at registry.modelcontextprotocol.io.  
    Submit `mcp.json` via PR.
    

* * *

What's next
-----------

We're investigating Stripe's [Machine Payments Protocol](https://docs.stripe.com/payments/machine/mpp)  
as a parallel rail (cards via Shared Payment Token + Tempo crypto), so  
customers without a crypto wallet can still pay per request.

If you're shipping an MCP server and want to compare notes — drop a  
comment or hit me on [LinkedIn](https://www.linkedin.com/posts/kenzo-arai-d_mcp-server-launched-call-chainanalyzer-share-7451274705354907648-dmF7?utm_source=share&utm_medium=member_desktop&rcm=ACoAACH0GxIBTlmp61nI3EaRcOYEmeekwneTQ4g).

* * *

_Originally posted at_ [_chain-analyzer.com_](https://chain-analyzer.com/news/mcp-server-launched)_._

---

*Originally published on [Building ChainAnalyzer](https://paragraph.com/@chainanalyzer/x402-usdc-mcp-server-mainnet)*
