| Component |
Role |
| Request Listener |
Watches the blockchain (via RPC or indexer) for incoming MCP requests. |
| AI / Model Backend |
Runs ML models or inference logic responding to queries. |
| Response Signer |
Cryptographically signs responses so contracts can verify authenticity. |
| Smart Contract |
Deployed on-chain to issue requests and validate responses. |
| Database / Cache |
Stores request logs, model outputs, and state to handle retries. |
From my experience, wiring the response signer correctly early avoids headaches later.
Setting Up a Basic EVM MCP Server
Let's walk through a minimal example using Node.js with ethers.js and a simple ML model stub.
Prerequisites
- Node.js v16+ installed
- Access to an Ethereum testnet RPC (e.g., Goerli)
- An EVM wallet private key for signing
Installing Dependencies
npm install ethers express body-parser
Minimal Server Example (pseudo-code)
import { ethers } from 'ethers';
import express from 'express';
const app = express();
app.use(express.json());
const privateKey = process.env.SIGNER_KEY!;
const wallet = new ethers.Wallet(privateKey);
// Dummy AI model - you would plug your inference here
function runModel(input: string): string {
return `Echo: ${input}`;
}
app.post('/mcp/request', async (req, res) => {
const { requestId, input } = req.body;
const output = runModel(input);
// Sign output with wallet
const signature = await wallet.signMessage(output);
res.json({ requestId, output, signature });
});
const PORT = 8080;
app.listen(PORT, () => console.log(`EVM MCP server running on port ${PORT}`));
Here, the server listens for MCP requests (presumably triggered via on-chain events). The AI model is a stub that just echoes input.
In real life, the server polls chain logs (or subscribes to events) to detect requests and then responds by sending a signed payload back on-chain or via a trusted channel.
For a more detailed guide, check the base-mcp-server-setup tutorial.
Supporting Multi-Chain in an EVM MCP Server
Running a multi-chain MCP server means your backend must:
- Listen to multiple EVM-compatible chains simultaneously
- Manage distinct wallet keys or session keys per chain
- Dispatch model responses with chain-specific signing
Managing Multi-Chain RPCs
A common pattern is to configure JSON-RPC endpoints for each supported chain in a config file or environment variables:
{
"chains": {
"mainnet": "https://mainnet.infura.io/v3/...",
"polygon": "https://polygon-rpc.com",
"bsc": "https://bsc-dataseed.binance.org"
}
}
Then instantiate ethers providers for each and use filters to watch MCP request events:
const providers = {
mainnet: new ethers.providers.JsonRpcProvider(rpcUrls.mainnet),
polygon: new ethers.providers.JsonRpcProvider(rpcUrls.polygon),
bsc: new ethers.providers.JsonRpcProvider(rpcUrls.bsc),
};
// Example: subscribe to MCPRequest event on each chain
for (const [chain, provider] of Object.entries(providers)) {
const mcpContract = new ethers.Contract(mcpAddress[chain], mcpAbi, provider);
mcpContract.on('MCPRequest', (requestId, input) => {
console.log(`[${chain}] MCP request ${requestId}:`, input);
// Process request per chain context
});
}
Key Management
You might want separate wallets or scoped session keys per chain to limit risk. For example, a session key with spending limits set via ERC-4337 or ERC-7579 can help control authorization — if the key is compromised, damage stays capped.
Response Signing
Responses must use the chain-specific wallet or key for signing. Signing protocols often employ EIP-712 typed data to make on-chain verification easier and less error-prone.
Example Toolchain for Multi-Chain MCP Server
| Tool |
Role |
Notes |
| ethers.js |
Multi-chain provider/client |
Supports all EVM chains |
| ElizaOS |
AI inference backend |
Can run AI models off-chain |
| Slither |
Smart contract static analysis |
Integrate for audit pipelines |
For a step-by-step multi-chain setup, our companion base-mcp-server-setup and mcp-server-tools-comparison pages are handy.
Security Considerations for Web3 MCP Servers
Running an MCP server connected to multiple chains increases the attack surface significantly. Here are some key points I always watch:
Private key storage: Never hardcode or store your signing keys in plaintext. Use hardware security modules (HSM), secure enclaves, or environment vaults.
Session keys & spending limits: Scoped session keys with explicit method whitelisting and spend caps reduce risk if a private key leaks.
Contract errors: Ensure your MCP smart contract validates signatures robustly, uses replay protection (chainId, nonce), and verifies data format.
Untrusted MCP servers: When integrating third-party MCP servers (e.g., hosted Claude instances), inspect how signing is done. Don’t blindly trust off-chain aggregation.
RPC endpoint security: If you self-host RPC nodes, secure access control and rate limiting are crucial to prevent DoS or data leaks.
For a deeper dive into best practices, see the mcp-server-security-best-practices page.
Deploying and Interacting with the MCP Smart Contract
The on-chain smart contract acts as the gatekeeper: issuing requests, verifying signed responses, and controlling payment flows if applicable.
Here’s a slimmed Solidity example illustrating the verification step:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract MCP {
address public signer;
event MCPRequest(uint256 indexed requestId, string input);
event MCPResponse(uint256 indexed requestId, string output);
constructor(address _signer) {
signer = _signer;
}
function requestModelData(uint256 requestId, string calldata input) external {
emit MCPRequest(requestId, input);
}
function submitModelResponse(
uint256 requestId,
string calldata output,
bytes calldata signature
) external {
bytes32 messageHash = keccak256(abi.encodePacked(requestId, output));
bytes32 ethSignedMsgHash =
keccak256(abi.encodePacked("\x19Ethereum Signed Message:
32", messageHash));
require(recoverSigner(ethSignedMsgHash, signature) == signer, "Invalid signature");
emit MCPResponse(requestId, output);
// Additional logic: payments, state updates, etc.
}
function recoverSigner(bytes32 _ethSignedMsgHash, bytes memory _signature) internal pure returns (address) {
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
return ecrecover(_ethSignedMsgHash, v, r, s);
}
function splitSignature(bytes memory sig)
internal
pure
returns (
bytes32 r,
bytes32 s,
uint8 v
) {
require(sig.length == 65, "invalid signature length");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
}
}
This contract doesn't cover replay protection or advanced authorization but is a solid baseline to build from.
Want a full deployment and client interaction example? Check out the mcp-wallet-integration tutorial for connecting agent wallets.
Common Pitfalls and Troubleshooting Tips
Missed event logs: Make sure your RPC provider supports archive logs if querying historical MCP requests.
Signature verification failure: Often caused by mismatched data payloads or incorrect EIP-191 vs EIP-712 formats.
Chain reorgs: MCP servers should handle short forks gracefully, avoiding double responses or missed requests.
Rate-limiting issues: Running multi-chain RPC listeners without rate limits can cause throttling and timeouts.
AI model latency: Slow inference models might delay response times, so cache frequent queries or precompute outputs.
If you hit a wall, refer to the mcp-server-troubleshooting page.
Summary and Next Steps
EVM MCP servers function as vital off-chain AI oracles crafting trustworthy inputs to smart contracts across chains. Setting one up involves balancing multi-chain event listening, reliable signing, robust security, and scalable AI/model inference.
In my experience, start simple — get a single chain working cleanly, signing responses, and verifying on-chain. From there, add more chains, tighten key management with session keys, and integrate real AI models replacing stubs.
If you want concrete tutorials on building MCP servers in Python or exploring agent payment protocols, check these internal guides:
Deploy smart contracts carefully, protect your private keys, and always run security static analysis tools like Slither before going mainnet.
Got questions about which tools to pick or specific multi-chain gotchas? Dive into our mcp-server-tools-comparison or security best practices to plan accordingly.
Building your own EVM MCP server isn't trivial but hugely rewarding — empowering next-gen on-chain AI applications. Ready to get your hands dirty? Start setting up your base server, and ping the community if you get stuck.
Happy building!