From ABI to MCP server in 5 minutes
Turn any smart contract ABI into a hosted MCP server your AI agent can actually use - without writing a line of glue code. A field guide for Web3 devs.
Every Web3 team I talk to has the same backlog item buried three rows down: “make it easy to use our contracts from Claude / Cursor / the agents our users run.” It sits there for months because the path from “here’s an ABI” to “a reliable tool an LLM can call” is a mini engineering project - not a weekend ticket.
It shouldn’t be. An ABI is a precise, machine-readable description of every callable function, input type, and output type. If you squint, it’s almost the thing an MCP client needs. The gap is interface polish, not protocol plumbing.
This post walks through how to close that gap in five minutes.
What is MCP, in 60 seconds
Model Context Protocol is an open standard (introduced by Anthropic, now adopted across the LLM ecosystem) for giving AI models access to tools, resources, and prompts. Think of it as the equivalent of USB for LLM context: one cable, many devices.
An MCP server exposes three things over a simple JSON-RPC transport:
- Tools - functions the model can call (read balances, quote a swap, submit a transaction)
- Resources - documents or data the model can read (a whitepaper, a pool’s current state)
- Prompts - pre-canned templates your users can invoke
For a smart contract, tools are the interesting part. Every read function becomes a query tool. Every write function becomes a transaction-building tool. The ABI already tells you exactly what the inputs and outputs look like.
Why the ABI is the right starting point
An ABI gives you, for free:
- Every function name and signature
- Input parameter types (address, uint256, bytes32, structs, enums)
- Output types and decoding rules
- Event signatures you can surface as resources
What it doesn’t give you, and what every serious MCP server needs:
- Intent - a
balanceOf(address)call is semantically “get the token balance for a wallet”, not “invoke function 0x70a08231” - Output shape - LLMs work better with 3 well-named fields than 47 encoded bytes
- Guardrails - which network, which chain ID, which RPC, which rate limit
- Safety on writes - you almost never want an LLM to sign a transaction; you want it to build one for a human to review
The five-minute version handles the first two automatically. The guardrails are configuration. Safety on writes is a design choice (ChainContext defaults to building unsigned transactions your user signs in their own wallet - more on that below).
The 5-minute path
Here’s the flow, end to end. It works for any EVM contract with a verified source on Etherscan.
1. Drop in the contract address. Paste it into the new-server form. We fetch the ABI from the block explorer automatically, including proxy resolution (EIP-1967 and the common transparent-proxy patterns).
2. Pick a recipe. Recipes are opinionated starting points - “ERC-20 token dashboard”, “Uniswap V3 pool analytics”, “DAO voting assistant”. Each recipe is just a YAML selection of which ABI functions to expose, with sensible defaults for output shaping. If none of them fit, start from “Blank” and pick functions yourself.
3. Review the generated tools. You’ll see one tool per selected function, with an auto-derived name, description, and output schema. You can edit any of them in place - more on that in the next section.
4. Set the network. Mainnet, a testnet, or a custom RPC. Every tool gets scoped to that chain; if a user tries to call with a wallet on the wrong chain, the server returns a structured error the LLM can recover from.
5. Deploy. Click publish. You get:
- A hosted MCP endpoint at a stable URL (
https://mcp.chaincontext.dev/<slug>) - Connection snippets for Claude Desktop, Cursor, and any OpenAI-compatible tool runner
- A
.well-known/mcp.jsonentry your users (or their agents) can discover - Usage analytics from the first call onward
At this point your contract has an MCP server your users can connect to Claude. The whole process is genuinely about five minutes for a standard ERC-20 or governance contract.
What a generated tool actually looks like
For a contract exposing balanceOf(address owner) view returns (uint256), the derived MCP tool looks like this:
{
"name": "get_token_balance",
"description": "Get the token balance for a specific wallet address.",
"inputSchema": {
"type": "object",
"properties": {
"owner": {
"type": "string",
"description": "Wallet address to check",
"pattern": "^0x[a-fA-F0-9]{40}$"
}
},
"required": ["owner"]
},
"outputSchema": {
"type": "object",
"properties": {
"balance": { "type": "string", "description": "Raw balance in base units" },
"decimals": { "type": "number" },
"formatted": { "type": "string", "description": "Human-readable balance" },
"symbol": { "type": "string" }
}
}
}
Three things worth noticing:
- The tool name is verb-first and readable (
get_token_balance, notbalanceOf). LLMs pick tools by embedding the description - the more natural-language it reads, the better the selection accuracy. - The input is validated at the edge: the address pattern catches malformed inputs before they hit your RPC.
- The output is reshaped. A raw
balanceOfreturns oneuint256. We decorate that withdecimalsand aformattedstring, so the model can answer “how much DAI does this wallet hold?” without an additional decimal-conversion round trip.
All three are the difference between a tool that “technically works” and one an agent actually picks correctly 95% of the time.
Making the tools actually usable
This is where the five-minute version ends and engineering judgment begins. The defaults are good; the best servers tune them. Three things to do per tool before you ship:
Rewrite the description in plain English. The ABI gives you nothing useful here. Write it as if for a reader who has never seen your contract. “Get the amount of token a wallet holds, in both raw base units and human-readable form” beats “balanceOf: returns uint256”.
Replace free strings with enums wherever possible. If your contract takes a network parameter, make it an enum of the chains you actually support. Models pick from a list far more reliably than they invent a value - and invented values are how you get errors at the RPC.
Trim the output. Raw on-chain returns are noisy. For a Uniswap V3 slot0 call you get sqrtPriceX96, tick, observationIndex, observationCardinality, observationCardinalityNext, feeProtocol, unlocked. Your users want the price. Give them the price. Fewer, better-named fields → faster, cheaper, more accurate agent runs.
The ChainContext editor lets you do all three without redeploying. Post-launch iteration on tool quality is what moves usage numbers; the initial generation just gets you over the cold-start hump.
Writes are different {#writes-are-different}
Read tools are safe. Write tools - anything that costs gas - are not. An agent that can sign transactions is an agent that can drain a wallet.
The pattern we default to, and the pattern any production MCP server for Web3 should use, is build unsigned, sign elsewhere. The tool returns a transaction payload (to, data, value, gas) that the user’s wallet signs - not the server, not the LLM. A walletconnect:// URI or a deep link gets rendered in the chat, the user taps, their wallet app handles the signature.
This keeps the LLM in its competence zone (constructing the right transaction) and out of the zones where being wrong costs real money. It’s also how you make a write-enabled MCP server legal to ship to users you’ve never met.
When to write custom code instead
Nothing here replaces real engineering judgment. Write a custom MCP server from scratch when:
- Your “contract” is actually ten contracts behind a complex router, and the on-the-wire ABI doesn’t reflect user intent
- You need to combine on-chain state with off-chain data (a price feed, an API, a cached index) in one tool response
- You have tool-call latency targets under 100ms and need to co-locate with a node
- You’re building a protocol-specific agent loop, not just surfacing contract reads to a generic assistant
The five-minute version is the best starting point for most contracts. It’s almost never the best finishing point for the 5% where bespoke matters.
Try it
If you want to skip straight to the generated server, paste any verified contract address into the new-server flow. You’ll have an MCP endpoint you can connect to Claude before your coffee goes cold.
More on the engineering side of this - block design, prompt patterns, agent-wallet UX - in Designing MCP tools for on-chain data. And the RSS feed is the best way to catch new posts as they land.