back to posts
#09 Part 2 2025-09-08 12 min

The Cross-Chain Tool Naming Registry: Universal Standards

How we made 17 blockchains speak the same language

Part 2 of the Journey: Discovering the Patterns - Learning Through Pain Previous: BigInt Testing Hell | Next: The MBPS v2.1 Standard

The Cross-Chain Tool Naming Registry: Universal Standards

How we made 17 blockchains speak the same language

Historical Context (November 2025): Developed September 2025 after manually building 17 servers revealed naming chaos. This work directly led to the MBPS v2.1 standard—pattern discovery through repetition.

Date: September 8, 2025 Author: Myron Koch & Claude Code Category: Standards & Architecture

The Tower of Babel

Before standardization, each blockchain server was its own universe:

// Ethereum server
'getBalance'
'check_balance'
'eth.getBalance'
'ethereum_balance'

// Solana server
'balance'
'getAccountBalance'
'sol_balance'
'solana.balance'

// Bitcoin server
'btc_get_balance'
'bitcoin_balance'
'get_wallet_balance'

Same operation. 12 different names. Total chaos.

The Naming Crisis

Real conversation we had:

User: "Check my balance on Ethereum and Solana"

Claude (using old servers):
  - Ethereum: Calling 'eth.getBalance'
  - Solana: Error - No tool 'sol.getBalance' found
  - Found: 'getAccountBalance'

User: "Why doesn't it just work?"
Us: "Because we're idiots who didn't standardize."

The MBPS v2.1 Naming Convention

The solution: {prefix}_{action}_{resource}

The Prefix Registry

Every blockchain gets ONE canonical prefix:

// The Sacred Registry
export const BLOCKCHAIN_PREFIXES = {
  // Mainnet prefixes
  'ethereum': 'eth',
  'bitcoin': 'btc',
  'solana': 'sol',
  'polygon': 'matic',
  'avalanche': 'avax',
  'binance-smart-chain': 'bsc',
  'arbitrum': 'arb',
  'optimism': 'op',
  'base': 'base',
  'near': 'near',
  'sui': 'sui',
  'aptos': 'apt',
  'cosmos-hub': 'atom',
  'osmosis': 'osmo',
  'xrp-ledger': 'xrp',
  'tron': 'tron',
  'bnb-chain': 'bnb',

  // Testnet suffixes
  'ethereum-sepolia': 'eth',      // Same prefix!
  'solana-devnet': 'sol',         // Same prefix!
  'bitcoin-testnet': 'btc',       // Same prefix!
  // ... all testnets use mainnet prefix
};

Why same prefix for testnet/mainnet?

The Action Registry

Standard verbs for all blockchains:

export const STANDARD_ACTIONS = {
  // Read operations
  'get': 'Retrieve data',
  'list': 'Get multiple items',
  'search': 'Find by criteria',
  'validate': 'Check validity',
  'estimate': 'Calculate cost',
  'simulate': 'Test without executing',

  // Write operations
  'send': 'Transfer native token',
  'transfer': 'Move tokens/assets',
  'create': 'Generate new entity',
  'deploy': 'Deploy contract',
  'mint': 'Create tokens/NFTs',
  'burn': 'Destroy tokens',

  // DeFi operations
  'swap': 'Exchange tokens',
  'add': 'Add to pool/stake',
  'remove': 'Withdraw from pool',
  'claim': 'Collect rewards',
  'stake': 'Lock for staking',
  'unstake': 'Unlock staked',

  // Smart contract operations
  'call': 'Execute contract function',
  'approve': 'Grant permission',
  'revoke': 'Remove permission',
};

The Resource Registry

Standard nouns for blockchain entities:

export const STANDARD_RESOURCES = {
  // Core blockchain resources
  'chain_info': 'Network information',
  'balance': 'Native token balance',
  'transaction': 'Transaction details',
  'block': 'Block information',
  'account': 'Account/address data',

  // Wallet resources
  'wallet': 'Wallet entity',
  'address': 'Address validation/generation',

  // Token resources
  'token_info': 'Token metadata',
  'token_balance': 'Token holdings',
  'allowance': 'Token permissions',

  // Contract resources
  'contract': 'Smart contract',
  'logs': 'Event logs',

  // NFT resources
  'nft': 'NFT metadata',
  'nft_collection': 'NFT collection',

  // DeFi resources
  'pool_info': 'Liquidity pool data',
  'rewards': 'Staking/farming rewards',

  // Network resources
  'gas_price': 'Transaction fees',
  'network_info': 'Network status',
  'validators': 'Validator information',
};

The Universal Tool Names

Result: Every blockchain has identical tool names for identical operations:

// All 17 servers have these exact tools:
'{prefix}_get_chain_info'
'{prefix}_get_balance'
'{prefix}_get_transaction'
'{prefix}_get_block'
'{prefix}_create_wallet'
'{prefix}_send_transaction'
'{prefix}_get_token_balance'
'{prefix}_transfer_token'
// ... 25 mandatory tools with identical patterns

Real Examples Across Chains

Getting Balance

// Ethereum
'eth_get_balance'

// Solana
'sol_get_balance'

// Bitcoin
'btc_get_balance'

// Cosmos Hub
'atom_get_balance'

// All follow same pattern!

Token Operations

// Ethereum ERC-20
'eth_transfer_token'

// Solana SPL
'sol_transfer_token'

// Polygon ERC-20
'matic_transfer_token'

// Same semantic meaning, same tool name pattern

DeFi Operations

// Ethereum Uniswap
'eth_swap_tokens'

// Solana Jupiter
'sol_swap_tokens'

// Osmosis DEX
'osmo_swap_tokens'

// Universal operation = universal name

The Naming Enforcement

We built validators to prevent deviation:

// scripts/validate-tool-names.ts

function validateToolName(toolName: string, serverPrefix: string): ValidationResult {
  const parts = toolName.split('_');

  if (parts.length < 3) {
    return {
      valid: false,
      error: `Tool name must have format: {prefix}_{action}_{resource}`,
      example: `${serverPrefix}_get_balance`
    };
  }

  const [prefix, action, ...resourceParts] = parts;

  // Validate prefix matches server
  if (prefix !== serverPrefix) {
    return {
      valid: false,
      error: `Prefix '${prefix}' doesn't match server prefix '${serverPrefix}'`,
      suggestion: `Use '${serverPrefix}_${action}_${resourceParts.join('_')}'`
    };
  }

  // Validate action is standard
  if (!STANDARD_ACTIONS[action]) {
    return {
      valid: false,
      error: `Non-standard action '${action}'`,
      standardActions: Object.keys(STANDARD_ACTIONS),
      suggestion: `Did you mean: ${findClosestAction(action)}?`
    };
  }

  const resource = resourceParts.join('_');

  // Validate resource is standard (or chain-specific)
  if (!STANDARD_RESOURCES[resource] && !isChainSpecificResource(resource, serverPrefix)) {
    return {
      valid: false,
      error: `Non-standard resource '${resource}'`,
      standardResources: Object.keys(STANDARD_RESOURCES),
      suggestion: `Did you mean: ${findClosestResource(resource)}?`
    };
  }

  return { valid: true };
}

Pre-commit Hook

#!/bin/bash
# .git/hooks/pre-commit

echo "Validating tool names..."

# Get list of all tools across all servers
node scripts/extract-all-tool-names.js > /tmp/all-tools.json

# Validate each tool name
node scripts/validate-tool-names.js /tmp/all-tools.json

if [ $? -ne 0 ]; then
  echo "❌ Tool naming validation failed!"
  echo "Fix tool names before committing."
  exit 1
fi

echo "✅ Tool naming validation passed"

The Migration Hell

Converting existing servers to standard naming:

Step 1: Inventory Current Names

// scripts/inventory-tools.ts
import fs from 'fs';
import path from 'path';

interface ToolInventory {
  server: string;
  currentName: string;
  suggestedName: string;
  references: string[];
}

function inventoryServer(serverPath: string): ToolInventory[] {
  const tools: ToolInventory[] = [];
  const indexPath = path.join(serverPath, 'src/index.ts');
  const content = fs.readFileSync(indexPath, 'utf8');

  // Extract tool registrations
  const toolRegex = /['"]([^'"]+)['"]\s*:\s*handle/g;
  let match;

  while ((match = toolRegex.exec(content)) !== null) {
    const currentName = match[1];
    const suggestedName = generateStandardName(currentName, serverPath);

    tools.push({
      server: path.basename(serverPath),
      currentName,
      suggestedName,
      references: findReferences(currentName, serverPath)
    });
  }

  return tools;
}

Step 2: Generate Migration Plan

// Output migration instructions
const inventory = inventoryServer('servers/testnet/ethereum-sepolia-mcp-server');

inventory.forEach(tool => {
  if (tool.currentName !== tool.suggestedName) {
    console.log(`
RENAME: ${tool.currentName} → ${tool.suggestedName}

Files to update:
${tool.references.map(ref => `  - ${ref}`).join('\n')}

Commands:
  # Rename handler function
  sed -i 's/handle${capitalizeFirst(tool.currentName)}/handle${capitalizeFirst(tool.suggestedName)}/g' ${tool.references.join(' ')}

  # Update tool registration
  sed -i "s/'${tool.currentName}'/'${tool.suggestedName}'/g" src/index.ts
    `);
  }
});

Step 3: Automated Renaming

// scripts/rename-tools.ts
async function renameTool(
  serverPath: string,
  oldName: string,
  newName: string
): Promise<void> {
  console.log(`Renaming ${oldName} → ${newName}`);

  // 1. Rename handler function
  const handlerOldName = `handle${toPascalCase(oldName)}`;
  const handlerNewName = `handle${toPascalCase(newName)}`;

  await replaceInFiles(serverPath, handlerOldName, handlerNewName);

  // 2. Rename tool file
  const oldFile = `src/tools/*/${toKebabCase(oldName)}.ts`;
  const newFile = `src/tools/*/${toKebabCase(newName)}.ts`;

  await renameFile(oldFile, newFile);

  // 3. Update tool registration
  await replaceInFile(
    `${serverPath}/src/index.ts`,
    `'${oldName}'`,
    `'${newName}'`
  );

  // 4. Update tests
  await replaceInFiles(
    `${serverPath}/tests`,
    `'${oldName}'`,
    `'${newName}'`
  );

  // 5. Update documentation
  await replaceInFiles(
    `${serverPath}`,
    oldName,
    newName,
    ['*.md', '*.json']
  );

  console.log(`✅ Renamed ${oldName} → ${newName}`);
}

The Alias Pattern

For backward compatibility during migration:

// src/index.ts
const toolHandlers: Record<string, Function> = {
  // New standard name
  'eth_get_balance': handleEthGetBalance,

  // Legacy aliases (deprecated)
  'getBalance': handleEthGetBalance,
  'eth.getBalance': handleEthGetBalance,
  'ethereum_balance': handleEthGetBalance,
};

// Warn on legacy usage
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name } = request.params;

  if (isLegacyName(name)) {
    logger.warn('Legacy tool name used', {
      legacy: name,
      standard: getStandardName(name),
      message: 'Please update to standard naming'
    });
  }

  return await toolHandlers[name](request.params.arguments, client);
});

The Documentation Standard

Every server’s README must include the tool naming reference:

## Tool Naming Convention

All tools follow MBPS v2.1 standard: `{prefix}_{action}_{resource}`

### Prefix
- **eth**: All Ethereum tools (mainnet and testnet)

### Available Tools

#### Core Operations
- `eth_get_chain_info` - Get network information
- `eth_get_balance` - Get ETH balance for address
- `eth_get_transaction` - Get transaction details
- `eth_get_block` - Get block information

#### Wallet Management
- `eth_create_wallet` - Generate new wallet
- `eth_import_wallet` - Import existing wallet
- `eth_validate_address` - Validate address format

[... complete tool listing]

The Benefits We Saw

1. Claude Can Generalize

User: "Check my balance on Ethereum and Solana"

Claude: I'll use {prefix}_get_balance for both chains.
  - eth_get_balance for Ethereum
  - sol_get_balance for Solana

[It just works!]

2. Tool Discovery

// Claude can infer tool names without documentation
User: "Can you swap tokens on Polygon?"

Claude: Looking for 'matic_swap_tokens'...
  - Found! Available DeFi tool.

3. Cross-Chain Workflows

// Claude understands cross-chain operations
User: "Bridge USDC from Ethereum to Polygon"

Claude: I'll need:
  1. eth_approve_token (approve bridge)
  2. eth_call_contract (initiate bridge)
  3. matic_get_transaction (check receipt)

4. Reduced Documentation

With standard naming, we don’t need to explain every tool:

The Exceptions

Some chains needed special resources:

// Solana-specific (Account-based)
'sol_get_account_info'    // Solana accounts != Ethereum accounts
'sol_create_token_account' // SPL tokens need dedicated accounts

// Bitcoin-specific (UTXO-based)
'btc_get_utxos'           // Bitcoin uses UTXOs not account balances
'btc_select_utxos'        // UTXO selection algorithm

// Cosmos-specific (IBC)
'atom_ibc_transfer'       // Inter-blockchain communication
'osmo_get_ibc_channels'   // IBC channel management

// XRP-specific
'xrp_create_escrow'       // XRP's native escrow
'xrp_create_trustline'    // XRP's trust system

Rule: Chain-specific features use standard actions with chain-specific resources.

The Testing Pattern

// tests/naming-convention.test.ts
describe('Tool Naming Convention', () => {
  const tools = getAllToolNames();
  const serverPrefix = getServerPrefix();

  it('all tools use correct prefix', () => {
    tools.forEach(tool => {
      expect(tool).toMatch(new RegExp(`^${serverPrefix}_`));
    });
  });

  it('all tools follow {prefix}_{action}_{resource} pattern', () => {
    tools.forEach(tool => {
      const parts = tool.split('_');
      expect(parts.length).toBeGreaterThanOrEqual(3);

      const [prefix, action, ...resourceParts] = parts;
      expect(prefix).toBe(serverPrefix);
      expect(STANDARD_ACTIONS).toHaveProperty(action);
    });
  });

  it('all actions are standard verbs', () => {
    tools.forEach(tool => {
      const action = tool.split('_')[1];
      expect(Object.keys(STANDARD_ACTIONS)).toContain(action);
    });
  });
});

The Numbers

Before standardization:

After standardization:

The Checklist

When adding a new tool:

The Future

The naming registry is now used for:

Standardization unlocked capabilities we didn’t anticipate.

References


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


Prerequisites

Next Steps

Deep Dives