How to Check If an Ethereum Address Is a Contract Using the Arbitrum MCP Server

The Arbitrum MCP Server exposes an is_contract tool that queries the eth_getCode RPC method to return true if bytecode exists at the address, or false if it is an externally-owned account (EOA).

The dewanshparashar/arbitrum-mcp repository implements a Model-Context-Protocol (MCP) server that simplifies Ethereum-compatible interactions. You can check if an Ethereum address is a contract using the Arbitrum MCP Server through a stateless tool that resolves RPC endpoints, executes bytecode lookups, and returns boolean results without requiring private keys or transaction signing.

How the is_contract Tool Works

The contract detection flow follows a deterministic sequence implemented across the server's architecture:

  1. Tool Invocation – The client sends a CallToolRequest with name: "is_contract" and the target address to the handler registered in src/index.ts.
  2. RPC Resolution – ArbitrumMCPServer.resolveRpcUrl determines the endpoint. It prioritizes user-supplied URLs, falls back to the default RPC URL, or resolves chain names via ChainLookupService (defined in src/services/chain-lookup.ts).
  3. Client Instantiation – The server creates a new EthereumAccountClient instance with the resolved RPC URL.
  4. Bytecode Retrieval – EthereumAccountClient.isContract invokes getCode(address), which performs an eth_getCode JSON-RPC call.
  5. Contract Detection – The implementation checks if the returned bytecode is non-empty (!== "0x"). If present, the address hosts a contract; otherwise, it is classified as an EOA.

Implementation Architecture

RPC Resolution and Chain Lookup

Before executing the bytecode query, the server must resolve the target network. The resolveRpcUrl method handles three resolution strategies:

  • Explicit rpcUrl argument provided in the tool call
  • Default RPC URL configured via the set_rpc_url tool
  • Chain name resolution (e.g., "Arbitrum One") through ChainLookupService

This resolution logic ensures the is_contract tool functions across any Ethereum-compatible network, not just Arbitrum chains.

The EthereumAccountClient Logic

The core detection mechanism resides in src/clients/ethereum-account-client.ts. The isContract method relies on the getCode helper, which executes the standard Ethereum JSON-RPC eth_getCode method. According to the source code, the boolean evaluation follows:

// Simplified logic from src/clients/ethereum-account-client.ts
const code = await this.getCode(address);
return code !== "0x";

This approach leverages the Ethereum protocol's guarantee that EOAs have no bytecode at their addresses, while smart contracts store their runtime bytecode at the deployment address.

Practical Code Examples

Set a Default RPC Endpoint

Configure a persistent RPC URL to avoid passing it with every request:

await server.callTool({
  name: "set_rpc_url",
  arguments: { rpcUrl: "https://arb1.arbitrum.io/rpc" }
});

Source: src/index.ts – set_rpc_url handler registration.

Query an Address via MCP Tool

Invoke the contract check using the server's tool interface:

const response = await server.callTool({
  name: "is_contract",
  arguments: {
    address: "0x1234...abcd",
    // Optional: override the default RPC or specify chain
    // rpcUrl: "https://arb1.arbitrum.io/rpc",
    // chainName: "Arbitrum One"
  }
});

console.log(response.content[0].text); 
// Output: "Is contract: true" or "Is contract: false"

Source: src/index.ts – is_contract case handler.

Direct Client Library Usage

For applications requiring programmatic access without the MCP server overhead, import the client directly:

import { EthereumAccountClient } from "./src/clients/ethereum-account-client.js";

const client = new EthereumAccountClient("https://arb1.arbitrum.io/rpc");
const isContract = await client.isContract("0x1234...abcd");
console.log(isContract); // boolean: true | false

Source: src/clients/ethereum-account-client.ts – isContract implementation.

Key Source Files

File Responsibility
src/clients/ethereum-account-client.ts Implements getCode and isContract methods; executes eth_getCode RPC calls.
src/index.ts Registers MCP tools including is_contract; orchestrates RPC resolution and response formatting.
src/services/chain-lookup.ts Provides chain metadata resolution, mapping human-readable names (e.g., "Arbitrum One") to RPC endpoints.

Summary

  • The is_contract tool in dewanshparashar/arbitrum-mcp provides a standardized MCP interface for determining address types.
  • Detection relies on the eth_getCode RPC method; non-empty bytecode indicates a smart contract, while 0x indicates an EOA.
  • The server supports flexible RPC resolution through explicit URLs, defaults, or chain name lookups via ChainLookupService.
  • Core logic resides in EthereumAccountClient.isContract within src/clients/ethereum-account-client.ts.
  • Clients can interact via the MCP server or import EthereumAccountClient directly for library-only usage.

Frequently Asked Questions

Does the is_contract tool work on Ethereum mainnet or only Arbitrum?

The tool works on any Ethereum-compatible chain. The chainName parameter in src/index.ts accepts identifiers like "Ethereum" or "Arbitrum One", and the ChainLookupService resolves these to appropriate RPC endpoints. You can also bypass chain names entirely by providing a direct rpcUrl pointing to Ethereum mainnet, Base, Optimism, or other EVM networks.

What happens if I don't specify an RPC URL or chain name?

The server follows a fallback hierarchy implemented in ArbitrumMCPServer.resolveRpcUrl. It first checks for a user-provided rpcUrl argument, then falls back to the default RPC URL configured via set_rpc_url. If neither exists, the tool returns an error prompting for network configuration.

How does the server differentiate between a contract and an EOA?

According to src/clients/ethereum-account-client.ts, the isContract method calls getCode(address) and evaluates whether the returned hex string equals "0x". Ethereum protocol specifications mandate that EOAs have empty code at their addresses, while deployed contracts store their runtime bytecode (even minimal proxy contracts return non-empty values).

Can I use the client library without running the full MCP server?

Yes. The EthereumAccountClient class in src/clients/ethereum-account-client.ts operates independently of the MCP protocol. Instantiate it directly with any EVM RPC endpoint to call isContract programmatically without setting up the server infrastructure or handling MCP request formatting.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →