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:
- Tool Invocation – The client sends a
CallToolRequestwithname: "is_contract"and the targetaddressto the handler registered insrc/index.ts. - RPC Resolution –
ArbitrumMCPServer.resolveRpcUrldetermines the endpoint. It prioritizes user-supplied URLs, falls back to the default RPC URL, or resolves chain names viaChainLookupService(defined insrc/services/chain-lookup.ts). - Client Instantiation – The server creates a new
EthereumAccountClientinstance with the resolved RPC URL. - Bytecode Retrieval –
EthereumAccountClient.isContractinvokesgetCode(address), which performs aneth_getCodeJSON-RPC call. - 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
rpcUrlargument provided in the tool call - Default RPC URL configured via the
set_rpc_urltool - 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_contracttool indewanshparashar/arbitrum-mcpprovides a standardized MCP interface for determining address types. - Detection relies on the
eth_getCodeRPC method; non-empty bytecode indicates a smart contract, while0xindicates an EOA. - The server supports flexible RPC resolution through explicit URLs, defaults, or chain name lookups via
ChainLookupService. - Core logic resides in
EthereumAccountClient.isContractwithinsrc/clients/ethereum-account-client.ts. - Clients can interact via the MCP server or import
EthereumAccountClientdirectly 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →