back to posts
#23 Part 4 2025-08-20 12 min

The Chain Info Breakthrough: Why Your Blockchain Tools Return Nothing

How we discovered that minimal implementations were hiding rich blockchain data

Part 4 of the Journey: Advanced Topics & Deep Dives Previous: The Token Overflow Crisis | Next: Building Interactive NFT Creation Workflows

The Chain Info Breakthrough: Why Your Blockchain Tools Return Nothing

How we discovered that minimal implementations were hiding rich blockchain data

Date: August 20, 2025 Author: Myron Koch & Claude Code Category: Architecture Insights

The Problem

We had 17 blockchain MCP servers, each with a get_chain_info tool. But they were returning almost nothing:

{
  "network": "testnet",
  "chainId": 1
}

Meanwhile, users expected comprehensive data like TPS, gas prices, validator counts, network health. We assumed blockchains just didn’t provide this data easily.

We were completely wrong.

The Discovery

On January 16, 2025, while enhancing four testnet servers (Arbitrum, TRON, NEAR, Sui), we made a shocking discovery:

Every blockchain provides rich data. We just weren’t asking for it.

Our implementations were minimal not because of blockchain limitations, but because we’d copy-pasted a minimal template everywhere. The chains had the data all along - we just needed to actually query for it.

The Pattern That Changed Everything

Instead of one lazy API call:

// BEFORE: The lazy way
async function getChainInfo() {
  const chainId = await provider.getNetwork();
  return { chainId };
}

We started fetching everything available in parallel:

// AFTER: The comprehensive way
async function getChainInfo() {
  const [
    network,
    blockNumber,
    block,
    gasPrice,
    priorityFee,
    blocks,
    validators,
    metrics,
    marketData
  ] = await Promise.all([
    provider.getNetwork(),
    provider.getBlockNumber(),
    provider.getBlock('latest'),
    provider.getGasPrice(),
    provider.send('eth_maxPriorityFeePerGas', []),
    getRecentBlocks(),
    getValidators(),
    calculateMetrics(),
    fetchMarketData()
  ]);

  return {
    network: {
      chainId: network.chainId,
      name: network.name,
      ensAddress: network.ensAddress
    },
    block: {
      current: blockNumber,
      timestamp: block.timestamp,
      transactions: block.transactions.length
    },
    gas: {
      price: formatUnits(gasPrice, 'gwei'),
      base: block.baseFeePerGas,
      priority: formatUnits(priorityFee, 'gwei')
    },
    metrics: {
      tps: calculateTPS(blocks),
      utilization: getNetworkUtilization(),
      totalTransactions: await getTotalTransactions()
    },
    market: {
      price: marketData.price,
      marketCap: marketData.marketCap,
      volume24h: marketData.volume24h
    }
  };
}

The Transformation

Arbitrum Sepolia - Before:

{
  "chainId": 421614
}

Arbitrum Sepolia - After:

{
  "network": {
    "chainId": 421614,
    "name": "arbitrum-sepolia",
    "rollup": "optimistic",
    "l1": "ethereum-sepolia"
  },
  "block": {
    "l2": 12345678,
    "l1": 5678901,
    "timestamp": 1737043921
  },
  "gas": {
    "l2Price": "0.1 gwei",
    "l1Price": "15 gwei",
    "sequencerStatus": "healthy"
  },
  "metrics": {
    "tps": 15.2,
    "utilization": "42%",
    "totalTransactions": 50000000
  },
  "market": {
    "price": "$0.487",
    "marketCap": "$1.2B",
    "volume24h": "$500M"
  }
}

Chain-Specific Discoveries

Each blockchain had unique data we were missing:

TRON

NEAR

Sui

Arbitrum

The Implementation Pattern

We established a universal pattern for comprehensive chain info:

interface ChainInfo {
  // Network basics
  network: {
    chainId: number;
    name: string;
    version?: string;
    protocol?: string;
  };

  // Block information
  block: {
    current: number;
    timestamp: number;
    transactions?: number;
    gasUsed?: string;
    gasLimit?: string;
  };

  // Gas/Fee data
  gas: {
    price: string;
    base?: string;
    priority?: string;
    unit: string;
  };

  // Network metrics
  metrics: {
    tps?: number;
    utilization?: string;
    totalTransactions?: number;
    activeValidators?: number;
  };

  // Market data (via CoinGecko)
  market?: {
    price: string;
    marketCap: string;
    volume24h: string;
    priceChange24h: string;
  };

  // Chain-specific extensions
  [key: string]: any;
}

Lessons Learned

1. Never Assume Limitations

We assumed blockchains didn’t provide data. Wrong. We just weren’t asking.

2. Parallel Fetching is Free Performance

Promise.all() turns 6 sequential calls (3 seconds) into 1 parallel call (0.5 seconds).

3. Copy-Paste Propagates Mediocrity

One minimal implementation got copied 17 times. The pattern spread like a virus.

4. Users Expect Bloomberg, Not Basic

People using blockchain tools expect comprehensive data. “Just the chain ID” doesn’t cut it.

5. Rich Data is Always There

Every blockchain has metrics, validators, parameters, and statistics. You just have to look.

The Ripple Effect

This discovery triggered a cascade of improvements:

  1. All servers enhanced: We retrofitted every blockchain server with comprehensive chain info
  2. Market data integration: Added CoinGecko to everything
  3. Standardized structure: One pattern across all chains
  4. User satisfaction: “Finally, useful information!”

Implementation Checklist

To implement comprehensive chain info:

Code Example: The Full Pattern

export async function handleGetChainInfo(
  args: any,
  client: BlockchainClient
): Promise<any> {
  try {
    // Parallel fetch everything
    const [
      networkInfo,
      latestBlock,
      gasPrice,
      validatorInfo,
      marketData
    ] = await Promise.all([
      client.getNetwork().catch(() => null),
      client.getLatestBlock().catch(() => null),
      client.getGasPrice().catch(() => null),
      client.getValidators().catch(() => null),
      fetchMarketData(client.token).catch(() => null)
    ]);

    // Calculate metrics
    const metrics = await calculateMetrics(client, latestBlock);

    // Build comprehensive response
    return {
      content: [{
        type: 'text',
        text: JSON.stringify({
          network: formatNetwork(networkInfo),
          block: formatBlock(latestBlock),
          gas: formatGas(gasPrice),
          validators: formatValidators(validatorInfo),
          metrics: metrics,
          market: marketData,
          timestamp: Date.now()
        }, null, 2)
      }]
    };
  } catch (error) {
    // Even on error, return what we can
    return {
      content: [{
        type: 'text',
        text: JSON.stringify({
          error: error.message,
          partial: true,
          network: { chainId: await client.getChainId() }
        }, null, 2)
      }]
    };
  }
}

What’s Next

We’re now auditing all 600+ tools across our ecosystem. How many are returning minimal data when they could be comprehensive?

The pattern is clear:

The Bigger Lesson

This breakthrough wasn’t about technology. It was about assumptions. We assumed blockchains were limited because our first implementation was limited. That assumption spread through copy-paste until it became “truth.”

Question your assumptions. Especially the ones that seem obviously true.

Sometimes the data is right there. You just have to ask for it.

References


This is part of our ongoing series documenting architectural patterns and insights from building the Blockchain MCP Server Ecosystem.


Prerequisites

Next Steps

Deep Dives