<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Weston G</title>
    <description>The latest articles on DEV Community by Weston G (@weston_g).</description>
    <link>https://dev.to/weston_g</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3925028%2Fb891451d-e418-44b3-a4cc-07e5d351995c.png</url>
      <title>DEV Community: Weston G</title>
      <link>https://dev.to/weston_g</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/weston_g"/>
    <language>en</language>
    <item>
      <title>Paste a wallet, get a personal airdrop verdict — and call the same logic from any LLM</title>
      <dc:creator>Weston G</dc:creator>
      <pubDate>Mon, 11 May 2026 20:06:05 +0000</pubDate>
      <link>https://dev.to/weston_g/paste-a-wallet-get-a-personal-airdrop-verdict-and-call-the-same-logic-from-any-llm-4ej0</link>
      <guid>https://dev.to/weston_g/paste-a-wallet-get-a-personal-airdrop-verdict-and-call-the-same-logic-from-any-llm-4ej0</guid>
      <description>&lt;h1&gt;
  
  
  Paste a wallet, get a personal airdrop verdict — and call the same logic from any LLM
&lt;/h1&gt;

&lt;p&gt;Two surfaces, one rule registry: a browser tool &lt;em&gt;and&lt;/em&gt; an MCP tool that share the exact same 14 hand-verified on-chain rules and return the exact same verdict shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Airdrop directories are full of "you might be eligible for 312 airdrops!" claims. They match on chain ("you've used Ethereum → you qualify for every Ethereum airdrop"), which is useless. The real eligibility lives on each project's contract or off-chain criteria.&lt;/p&gt;

&lt;p&gt;So I built a tool that does the opposite: a tiny number of hand-verified rules, each tied to a specific entry, evaluated against the wallet you paste. No signature, no signup, no server-side address logging — just RPC calls fanned out from the browser. And because the same rule registry feeds an MCP tool, any LLM client (Claude Desktop, Cursor, etc.) can call it directly without scraping the site.&lt;/p&gt;

&lt;h2&gt;
  
  
  The browser tool
&lt;/h2&gt;

&lt;p&gt;Live at &lt;a href="https://web3-discover.vercel.app/tools/eligibility" rel="noopener noreferrer"&gt;web3-discover.vercel.app/tools/eligibility&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Paste an EVM and/or Solana address. The page evaluates 14 rules against 42 curated entries and buckets the results into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;✅ Likely eligible&lt;/strong&gt; — rule passes (e.g. &lt;code&gt;eth_getTransactionCount&lt;/code&gt; on Linea ≥ 1, or you hold ≥ 1 PENDLE)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🟡 Keep farming&lt;/strong&gt; — partial: you've started but not crossed the threshold (e.g. 2 Arbitrum tx when 5 are needed for Reya / Ostium)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⏭ Not relevant&lt;/strong&gt; — rule fails outright&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔍 Manual check&lt;/strong&gt; — entry lives on a chain we can't reach from the browser (Monad, MegaETH, Hyperliquid, Sui-based) or has off-chain criteria (KYC, Discord, Galxe quests)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try &lt;a href="https://web3-discover.vercel.app/tools/eligibility?evm=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&amp;amp;sol=9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM" rel="noopener noreferrer"&gt;vitalik.eth + a public Solana wallet&lt;/a&gt; — resolves in 2-5 seconds, 9 eligible at the time of writing.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the browser side stays cheap
&lt;/h3&gt;

&lt;p&gt;Every entry-page CTA deep-links to &lt;code&gt;/tools/eligibility?focus=&amp;lt;slug&amp;gt;&lt;/code&gt;. A banner at the top of the results page names the entry the visitor came from, then after evaluation the matching card is scrolled into view and pulse-highlighted with a CSS keyframe. This costs zero extra requests — the focus logic is pure DOM-after-render.&lt;/p&gt;

&lt;p&gt;Total RPC requests = one batched JSON-RPC POST per EVM chain (so 6 max for an EVM address) plus per-mint Solana calls. Each batch bundles &lt;code&gt;eth_getTransactionCount&lt;/code&gt; + every &lt;code&gt;eth_call(balanceOf)&lt;/code&gt; we need for that chain, so adding more rules on Ethereum costs zero additional requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MCP tool
&lt;/h2&gt;

&lt;p&gt;Same logic, exposed at &lt;code&gt;https://web3-discover.vercel.app/api/mcp&lt;/code&gt; as the fourth tool of the web3-discover MCP server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://web3-discover.vercel.app/api/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "jsonrpc": "2.0",
    "id": 7,
    "method": "tools/call",
    "params": {
      "name": "check_eligibility",
      "arguments": { "evm": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns the same verdict shape — &lt;code&gt;eligible[]&lt;/code&gt;, &lt;code&gt;partial[]&lt;/code&gt;, &lt;code&gt;manual[]&lt;/code&gt;, &lt;code&gt;unavailable[]&lt;/code&gt;, &lt;code&gt;skip[]&lt;/code&gt; — but as machine-readable JSON each LLM client can render however it likes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"evm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xd8dA…6045"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rulesEvaluated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"totalTracked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"counts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eligible"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"partial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"manual"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"unavailable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eligible"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"base-coinbase-l2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Base"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"chain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Base (Coinbase L2)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"verdictDetail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"70 tx on Base (≥ 1 required)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"officialUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://base.org"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Drop-in for Claude Desktop / Cursor
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"web3-discover"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://web3-discover.vercel.app/api/mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask: &lt;em&gt;"What airdrops am I eligible for? My wallet is 0x…"&lt;/em&gt; and the model calls &lt;code&gt;check_eligibility&lt;/code&gt; directly. No scraping, no flaky HTML parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-file pattern that keeps both surfaces honest
&lt;/h2&gt;

&lt;p&gt;Two surfaces means two ways for the rules to drift. The fix is one source of truth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/data/
  eligibility-rules.json   # source of truth
  eligibility-rules.ts     # typed wrapper for the Astro page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And during prebuild:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// scripts/build-airdrops-snapshot.mjs&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/data/eligibility-rules.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api/_eligibility-rules.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP serverless function reads the JSON at cold start; the Astro page imports the typed &lt;code&gt;.ts&lt;/code&gt; wrapper. Same data, two consumers, zero drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters as a wedge
&lt;/h2&gt;

&lt;p&gt;Most directory sites optimise for "discover something new". This optimises for the moment after you've already heard of a project: &lt;em&gt;"do I, this wallet I'm holding, actually need to act on this?"&lt;/em&gt; That question doesn't compete with newsletters or Twitter — it complements them.&lt;/p&gt;

&lt;p&gt;And exposing the same logic over MCP means the directory gets pulled into LLM chats &lt;em&gt;as the user is making decisions&lt;/em&gt;, not after the fact. The two surfaces feed each other: the browser tool drives organic / shared discovery; the MCP tool drives in-chat utility when a user has Claude open anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browser:&lt;/strong&gt; &lt;a href="https://web3-discover.vercel.app/tools/eligibility" rel="noopener noreferrer"&gt;web3-discover.vercel.app/tools/eligibility&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP:&lt;/strong&gt; &lt;code&gt;https://web3-discover.vercel.app/api/mcp&lt;/code&gt; — JSON-RPC 2.0, &lt;code&gt;tools/list&lt;/code&gt; returns four tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; the curated entries themselves live in a separate CC0 repo at &lt;a href="https://github.com/SolvoHQ/web3-discover-data" rel="noopener noreferrer"&gt;SolvoHQ/web3-discover-data&lt;/a&gt;, auto-refreshed daily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open to pull requests adding new rules — each one is ~5 lines in the JSON registry.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>crypto</category>
      <category>mcp</category>
    </item>
    <item>
      <title>I shipped an MCP server for crypto airdrops — install in 1 config line</title>
      <dc:creator>Weston G</dc:creator>
      <pubDate>Mon, 11 May 2026 14:13:59 +0000</pubDate>
      <link>https://dev.to/weston_g/i-shipped-an-mcp-server-for-crypto-airdrops-install-in-1-config-line-1pl7</link>
      <guid>https://dev.to/weston_g/i-shipped-an-mcp-server-for-crypto-airdrops-install-in-1-config-line-1pl7</guid>
      <description>&lt;p&gt;I shipped a tiny MCP server last week that turns a curated crypto airdrop directory into 3 tools any LLM client (Claude Desktop, Cursor, Continue, Windsurf) can call directly.&lt;/p&gt;

&lt;p&gt;This post is the install snippet + a screenshot of it returning real data — so you can decide in 30 seconds whether it's worth one config line.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;p&gt;Three tools, no signup, no API key, no rate limit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list_active_airdrops(chain?, risk?, sort_by?, limit?)&lt;/code&gt; — browse 32 vetted active airdrops, filter by chain (Solana, Base, Ethereum…) or risk level (verified / unverified)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_airdrop(slug)&lt;/code&gt; — full details for one campaign: action steps, weekly effort, cost floor, risk notes, official URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;check_wallet(addr)&lt;/code&gt; — paste an EVM or Solana address, fan out to 7 public RPCs (Ethereum, Base, Linea, Arbitrum, Polygon, BSC, Solana), and surface every tracked airdrop whose chain you've already touched&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hosted, JSON-RPC 2.0 over HTTP, CORS-open. Endpoint: &lt;code&gt;https://web3-discover.vercel.app/api/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install in Claude Desktop or Cursor
&lt;/h2&gt;

&lt;p&gt;Add this one block to your MCP config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"web3-discover"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://web3-discover.vercel.app/api/mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Restart your client. Ask: &lt;em&gt;"What active airdrops can I farm on Solana this week with under $100 capital?"&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it returns (live)
&lt;/h2&gt;

&lt;p&gt;Here's the actual JSON-RPC 2.0 response from a real &lt;code&gt;tools/call&lt;/code&gt; against the live endpoint — pulled the moment I wrote this article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsbbeb4nn5vw1r3t7tjf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsbbeb4nn5vw1r3t7tjf.png" alt="web3-discover MCP returning 3 real airdrop entries via JSON-RPC 2.0" width="800" height="873"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verify it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://web3-discover.vercel.app/api/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_active_airdrops","arguments":{"limit":3,"sort_by":"added"}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get back structured entries: slug, project, chain, blurb, deadline, effort estimate, cost floor, risk flag (&lt;code&gt;verified&lt;/code&gt; means the team has publicly confirmed TGE plans; &lt;code&gt;unverified&lt;/code&gt; means speculative), and the official URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is interesting (beyond "another MCP server")
&lt;/h2&gt;

&lt;p&gt;I run a curated airdrop directory at &lt;a href="https://web3-discover.vercel.app" rel="noopener noreferrer"&gt;web3-discover.vercel.app&lt;/a&gt;. Every entry is hand-vetted, scam-filtered, and re-checked weekly. The web surface works well for humans who already know about it — but most retail farmers ask LLMs first now.&lt;/p&gt;

&lt;p&gt;So I exposed the directory as MCP tools. Three things this unlocks that the web UI alone can't:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Natural-language filtering.&lt;/strong&gt; "What's verified-only, ongoing, costs gas only?" — the LLM picks &lt;code&gt;list_active_airdrops&lt;/code&gt;, filters by &lt;code&gt;risk=verified&lt;/code&gt;, sorts by &lt;code&gt;deadline&lt;/code&gt;, narrates the result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wallet-aware advice.&lt;/strong&gt; &lt;code&gt;check_wallet&lt;/code&gt; returns which chains your wallet has touched plus the entries on those chains. The LLM combines that with your stated risk tolerance and tells you what to prioritize, not just what exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition with other MCP servers.&lt;/strong&gt; If you've got a wallet MCP server + this one, the LLM can chain "what airdrops am I close to qualifying for, given my actual on-chain history?"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No tracking. The directory has no signup. The check_wallet tool just reads from public RPCs — no address is logged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why hosted, not stdio?
&lt;/h2&gt;

&lt;p&gt;Most MCP servers ship as &lt;code&gt;npx some-package&lt;/code&gt; that runs on your machine. I went hosted (HTTP transport via &lt;code&gt;mcp-remote&lt;/code&gt; bridge) for one reason: &lt;strong&gt;the data changes weekly&lt;/strong&gt;. A stdio package would have to re-download the directory or hit an API anyway. Hosting it means the data refreshes when I do a freshness sweep, not when you remember to &lt;code&gt;npm update&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tradeoff: you trust me to keep the endpoint up. I run a freshness sweep weekly (the directory page shows a "last verified" date per entry). If you'd rather self-host, the &lt;a href="https://github.com/SolvoHQ/web3-discover" rel="noopener noreferrer"&gt;source is MIT&lt;/a&gt; and the MCP handler is a single ~250-line file (&lt;code&gt;api/mcp.mjs&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  What works, what doesn't, what's next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Works today:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 3 tools verified end-to-end on Claude Desktop and Cursor with &lt;code&gt;mcp-remote&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;32 active entries, 7-chain wallet check&lt;/li&gt;
&lt;li&gt;No auth, no rate limit at current load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Doesn't yet:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No eligibility scoring — &lt;code&gt;check_wallet&lt;/code&gt; is a presence check, not an "are you actually eligible?" check. Most airdrops have additional criteria (volume, tx count, NFT holdings) that I'd need to wire per-entry.&lt;/li&gt;
&lt;li&gt;No notifications — if a deadline shifts or an entry gets removed, you'll only see it next time you query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;subscribe_deadlines(addr)&lt;/code&gt; returning an iCalendar feed so deadlines land in your calendar&lt;/li&gt;
&lt;li&gt;Per-entry eligibility checks where the airdrop has a public scoring API&lt;/li&gt;
&lt;li&gt;Adding the directory as an MCP resource (not just tools) so LLMs can browse without explicit calls&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Drop the config block above into Claude Desktop (&lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt; on macOS) or Cursor (Settings → MCP). Ask "What airdrops are worth farming this week?"&lt;/p&gt;

&lt;p&gt;If it's useful, the directory itself lives at &lt;a href="https://web3-discover.vercel.app" rel="noopener noreferrer"&gt;web3-discover.vercel.app&lt;/a&gt; — same data, human-shaped.&lt;/p&gt;

&lt;p&gt;If something breaks or you want a tool added, &lt;a href="https://github.com/SolvoHQ/web3-discover/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Astro + Vercel serverless. Three days, ~250 LOC for the MCP handler. The hardest part wasn't the code — it was making sure the underlying directory data was actually honest. (No paid placements pretending to be listings.)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>crypto</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a daily-refreshed, scam-flagged airdrop directory with Astro + IndexNow + GoatCounter</title>
      <dc:creator>Weston G</dc:creator>
      <pubDate>Mon, 11 May 2026 12:57:42 +0000</pubDate>
      <link>https://dev.to/weston_g/building-a-daily-refreshed-scam-flagged-airdrop-directory-with-astro-indexnow-goatcounter-l80</link>
      <guid>https://dev.to/weston_g/building-a-daily-refreshed-scam-flagged-airdrop-directory-with-astro-indexnow-goatcounter-l80</guid>
      <description>&lt;p&gt;I've been building &lt;strong&gt;&lt;a href="https://web3-discover.vercel.app" rel="noopener noreferrer"&gt;web3-discover&lt;/a&gt;&lt;/strong&gt; — a hand-vetted, scam-flagged directory of currently-claimable web3 airdrops — and I wanted to share the static-site + agent-driven pipeline behind it, because the architecture is unusual: there's no backend, no database, no editor login. Everything is git-tracked Markdown, every deploy is a static build, and content is refreshed daily by an autonomous workflow rather than human curators.&lt;/p&gt;

&lt;p&gt;Live URL: &lt;strong&gt;&lt;a href="https://web3-discover.vercel.app" rel="noopener noreferrer"&gt;https://web3-discover.vercel.app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The wedge (why "fewer entries" is the feature)
&lt;/h2&gt;

&lt;p&gt;The crypto-airdrop site space is dominated by SEO-juiced aggregators listing 300+ "airdrops" — most of which are expired, scams, or paid placements pretending to be listings. The wedge is the opposite: ~30 hand-vetted entries, each with a concrete action, cost floor, deadline, risk flag, and a &lt;code&gt;lastChecked&lt;/code&gt; date. If we can't verify an entry, we drop it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack (and why each piece)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Astro 5&lt;/strong&gt; — chosen specifically because content collections give you a typed schema for markdown frontmatter, and the output is dead-static HTML. No client JS in the critical path. Every entry page is a 9KB document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/content/config.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;airdrops&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ethereum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Solana&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Arbitrum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;costFloor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;     &lt;span class="c1"&gt;// "$0", "$50", "gas-only"&lt;/span&gt;
    &lt;span class="na"&gt;deadline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;      &lt;span class="c1"&gt;// ISO date or "ongoing"&lt;/span&gt;
    &lt;span class="na"&gt;riskFlag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unverified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;suspected-scam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="na"&gt;lastChecked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;officialUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;addedOn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an entry breaks schema, the build fails loudly. That's the whole content-CI layer — no DB migrations to run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vercel&lt;/strong&gt; for hosting — &lt;code&gt;vercel --prod&lt;/code&gt; from CLI. Free tier handles plenty of traffic. Sitemap pinned to &lt;code&gt;@astrojs/sitemap@3.2.1&lt;/code&gt; because newer versions broke our Astro 5 build chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON-LD&lt;/strong&gt; for structured data — &lt;code&gt;Organization&lt;/code&gt; + &lt;code&gt;WebSite&lt;/code&gt; on every page (in &lt;code&gt;Base.astro&lt;/code&gt; layout), &lt;code&gt;ItemList&lt;/code&gt; on the airdrops index page, &lt;code&gt;Article&lt;/code&gt; schema on each entry page (with &lt;code&gt;datePublished&lt;/code&gt; from frontmatter &lt;code&gt;addedOn&lt;/code&gt;, publisher logo, &lt;code&gt;mainEntityOfPage&lt;/code&gt;, &lt;code&gt;dateModified&lt;/code&gt; sourced from &lt;code&gt;lastChecked&lt;/code&gt;). Verified live via curl — the JSON parses cleanly with &lt;code&gt;python3 -m json.tool&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RSS&lt;/strong&gt; at &lt;code&gt;/rss.xml&lt;/code&gt; using &lt;code&gt;@astrojs/rss&lt;/code&gt;. Each item carries the full action/effort/cost/deadline/risk block — readable in a feed reader without clicking through. GUIDs are entry URLs (canonical).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IndexNow&lt;/strong&gt; for crawler push notifications — three endpoints pinged on every content update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://api.indexnow.org/indexnow&lt;/code&gt; → 202&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://www.bing.com/indexnow&lt;/code&gt; → 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://yandex.com/indexnow&lt;/code&gt; → 202 (&lt;code&gt;{"success":true}&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The verification key is a file at &lt;code&gt;https://web3-discover.vercel.app/&amp;lt;hex&amp;gt;.txt&lt;/code&gt;. &lt;strong&gt;Don't delete that file — Bing re-validates on subsequent pings.&lt;/strong&gt; I learned that the hard way trying to "clean up" the public dir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GoatCounter&lt;/strong&gt; for analytics. Privacy-friendly, no cookies, no consent banner needed. Embed is a 5-line &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;. The dashboard lives at &lt;code&gt;&amp;lt;workspace&amp;gt;.goatcounter.com&lt;/code&gt;. Counts pageviews + referrers, exactly what I need to see whether a distribution channel actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent-refresh loop (the unusual part)
&lt;/h2&gt;

&lt;p&gt;The site is maintained by an autonomous workflow (Claude Code agent ticks running on a 30-second heartbeat). Every wake the agent reads the current Markdown entries, checks &lt;code&gt;lastChecked&lt;/code&gt; against &lt;code&gt;Date.now() - 7d&lt;/code&gt;, and queues stale entries for re-verification:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch the project's official Twitter or docs page.&lt;/li&gt;
&lt;li&gt;Re-confirm the deadline string.&lt;/li&gt;
&lt;li&gt;Re-confirm the action steps still match what the project requires.&lt;/li&gt;
&lt;li&gt;If anything has changed: update the frontmatter, bump &lt;code&gt;lastChecked&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If the project has TGE'd (token generation event passed) or the airdrop is over: remove the entry from &lt;code&gt;src/content/airdrops/&lt;/code&gt; entirely.&lt;/li&gt;
&lt;li&gt;Re-build, redeploy, ping IndexNow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the first sweep (22 → 32 entries) the agent caught three of my own false assumptions: Monad / MegaETH / Plasma are post-TGE, not pre-TGE. Sonic Labs Season 2 ended November 2025 — dropped. The point isn't that the agent is smarter than me; it's that running the verification loop on a 7-day cycle instead of "when I feel like it" surfaces drift fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-entry conversion pivot (a real lesson)
&lt;/h2&gt;

&lt;p&gt;Initial v1 had a &lt;code&gt;/tools/swap&lt;/code&gt; page with Jupiter Plugin (Solana) and Jumper deeplink (EVM) as a monetization path — integrator-fees on swaps. Every airdrop entry page had a "Sell when claimed" CTA pointing at &lt;code&gt;/tools/swap?token=X&amp;amp;chain=Y&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The bug: &lt;strong&gt;the URL params were read by NO code.&lt;/strong&gt; Every sell-CTA from 32 entry pages dumped users on a USDC-default swap. The single biggest pre-traffic conversion gap, sitting in production for ~24 hours before I caught it.&lt;/p&gt;

&lt;p&gt;Fix: a slug→target registry mapping each entry's &lt;code&gt;chain + symbol&lt;/code&gt; to a concrete token address. For Solana, that's the SPL mint passed into &lt;code&gt;Jupiter.formProps.initialInputMint&lt;/code&gt;. For EVM, &lt;code&gt;?fromChain=…&amp;amp;fromToken=…&lt;/code&gt; on the Jumper deeplink. 12 verified Solana mints + 20 EVM token addresses are now hardcoded into a TypeScript const. Building this took ~25 minutes. The fact that an agent let it ship undetected for 24h is the cautionary tale — verification-before-completion is non-negotiable when your reviewer-of-last-resort is the same process that shipped the change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guides pillar (SEO + risk-awareness)
&lt;/h2&gt;

&lt;p&gt;Three evergreen guides live at &lt;code&gt;/guides/scams&lt;/code&gt;, &lt;code&gt;/guides/taxes&lt;/code&gt;, &lt;code&gt;/guides/wallet-hygiene&lt;/code&gt;. Every airdrop entry internally links to &lt;strong&gt;two&lt;/strong&gt; risk-matched guides (a &lt;code&gt;suspected-scam&lt;/code&gt; entry links to &lt;code&gt;/guides/scams&lt;/code&gt;; a high-deadline entry links to &lt;code&gt;/guides/wallet-hygiene&lt;/code&gt;). Indexable surface went from 35 to 39 URLs, every entry page gains internal-link equity.&lt;/p&gt;

&lt;p&gt;The choice was deliberate: I'd rather have 3 deep guides than 30 shallow ones. Crawlers reward dwell time, and a 1500-word scams guide with concrete patterns ("if the claim URL doesn't match the project's official Twitter pinned tweet, walk away") does that better than 30x 200-word listicles.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell anyone building in this category
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anti-features are the differentiation.&lt;/strong&gt; "No wallet connect, no claim links, no paid listings" is not a copy gimmick — it's the reason I trust my own site, and it's the reason a user might. The competitive moat in any junk-flooded category is being the one that says "no" to the easy money.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Static + agent-refresh &amp;gt; headless CMS.&lt;/strong&gt; A headless CMS gives editors a UI. I don't have editors. The git tree is the database; the deploy is the publish step; the agent is the editor. If your contributor list is 1, this collapses to dramatically less infra than "Strapi + Postgres + worker queue".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JSON-LD is free SEO money.&lt;/strong&gt; Article schema with &lt;code&gt;dateModified&lt;/code&gt; sourced from &lt;code&gt;lastChecked&lt;/code&gt; tells Google "this is a fresh page". Cost: 12 lines per layout file. Benefit: every entry shows the freshness signal in SERPs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IndexNow ≠ Google Search Console.&lt;/strong&gt; GSC verification is an interactive OAuth flow with no zero-account path I could find. IndexNow accepts a static key file and gets you Bing + Yandex (the latter is non-trivial for crypto SEO, where Russian and Eastern European traffic is real). Don't burn hours on GSC; ship IndexNow and move on.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Site: &lt;strong&gt;&lt;a href="https://web3-discover.vercel.app" rel="noopener noreferrer"&gt;https://web3-discover.vercel.app&lt;/a&gt;&lt;/strong&gt; — currently 32 active entries, deadline-sorted, refreshed today.&lt;/p&gt;

&lt;p&gt;If you spot an entry that's wrong, broken, or scammy I should flag harder — open an issue at the repo (linked in the footer). The whole content tree is public Markdown — feedback round-trips fast.&lt;/p&gt;

&lt;p&gt;Tags: &lt;code&gt;#astro&lt;/code&gt;, &lt;code&gt;#web3&lt;/code&gt;, &lt;code&gt;#staticsite&lt;/code&gt;, &lt;code&gt;#seo&lt;/code&gt;, &lt;code&gt;#crypto&lt;/code&gt;&lt;/p&gt;

</description>
      <category>astro</category>
      <category>web3</category>
      <category>webdev</category>
      <category>crypto</category>
    </item>
  </channel>
</rss>
