Safety Features¶
abi-to-mcp is designed with safety as a primary concern. This page describes all the built-in protections.
Safety Philosophy¶
Principle: No transaction should execute without explicit user intent.
Smart contract interactions are irreversible. Once a transaction is confirmed, it cannot be undone. abi-to-mcp provides multiple layers of protection:
graph TD
A[User Request] --> B{Simulation}
B -->|Pass| C{User Confirmation}
B -->|Fail| D[Show Error]
C -->|Confirmed| E{Private Key?}
C -->|Rejected| F[Abort]
E -->|Yes| G[Execute]
E -->|No| H[Error: No Key]
style B fill:#2196F3,color:#fff
style C fill:#FF9800,color:#fff
style G fill:#F44336,color:#fff
Layer 1: Simulation by Default¶
All write operations simulate before executing:
@mcp.tool()
async def transfer(
to: str,
amount: str,
simulate: bool = True # Default: simulation only
) -> dict:
"""Transfer tokens."""
if simulate:
return await simulate_transaction(...)
else:
return await execute_transaction(...)
What Simulation Shows¶
{
"success": true,
"would_succeed": true,
"gas_estimate": 65000,
"gas_price_gwei": 25.5,
"estimated_cost_eth": "0.0016575",
"estimated_cost_usd": "$3.15",
"result": true
}
Simulation Catches¶
- ✅ Insufficient balance
- ✅ Insufficient allowance
- ✅ Contract reverts
- ✅ Invalid parameters
- ✅ Access control failures
Enabling Execution¶
Must explicitly set simulate=False:
# This WILL execute
result = await transfer(
to="0x...",
amount="1000000000000000000",
simulate=False # Explicit opt-in required
)
Layer 2: Read-Only Mode¶
Generate servers that cannot write:
This completely excludes:
- All nonpayable functions
- All payable functions
- No private key handling code
Perfect for: - Exploration and research - Public-facing servers - Analytics dashboards
Layer 3: No Embedded Keys¶
Private keys are never stored in generated code:
This means: - No keys in source control - No keys in generated files - Keys only loaded at runtime - Missing key = graceful error
Missing Key Behavior¶
if not PRIVATE_KEY:
return {
"error": "PRIVATE_KEY not configured",
"message": "Write operations require a private key. "
"Set the PRIVATE_KEY environment variable."
}
Layer 4: Transaction Validation¶
Before any transaction:
Address Validation¶
Amount Validation¶
# Ensures positive integer
amount = int(amount_str)
if amount < 0:
raise ValueError("Amount cannot be negative")
Gas Validation¶
# Prevents runaway gas costs
if gas_estimate > MAX_GAS_LIMIT:
raise ValueError(f"Gas estimate {gas_estimate} exceeds limit")
Layer 5: Network Confirmation¶
Always confirms the network before writing:
# Verify chain ID matches expected
chain_id = await w3.eth.chain_id
if chain_id != EXPECTED_CHAIN_ID:
raise NetworkMismatchError(
f"Expected chain {EXPECTED_CHAIN_ID}, got {chain_id}"
)
Safety Configuration¶
Environment Variables¶
| Variable | Purpose | Default |
|---|---|---|
PRIVATE_KEY |
Transaction signing | None (disabled) |
SIMULATION_DEFAULT |
Default simulation mode | true |
READ_ONLY_MODE |
Disable all writes | false |
MAX_GAS_LIMIT |
Maximum gas per tx | 500000 |
REQUIRE_CONFIRMATION |
Require explicit confirm | true |
Generation Options¶
# Maximum safety
abi-to-mcp generate ./abi.json \
--read-only \
--no-events
# Standard safety (simulation default)
abi-to-mcp generate ./abi.json \
--simulate
# Reduced safety (not recommended)
abi-to-mcp generate ./abi.json \
--no-simulate
Best Practices¶
For AI Assistants¶
Always Explain Risks
When a user asks to execute a transaction:
- Explain what the transaction will do
- Show the estimated gas cost
- Confirm the user wants to proceed
- Execute with
simulate=Falseonly after confirmation
For Developers¶
Never Disable Simulation Globally
Always keep simulate=True as the default. Let users explicitly opt-in to execution.
For Production¶
Dedicated Wallets
Never use a wallet containing significant funds. Create a dedicated wallet for MCP interactions with only the funds needed for operations.
Error Handling¶
All errors are caught and returned safely:
try:
result = await execute_transaction(...)
except InsufficientFundsError as e:
return {"error": "insufficient_funds", "message": str(e)}
except ContractRevertError as e:
return {"error": "contract_revert", "message": str(e)}
except Exception as e:
return {"error": "unknown", "message": str(e)}
Audit Trail¶
All operations are logged:
logger.info(f"Simulating transfer: to={to}, amount={amount}")
logger.warning(f"Executing transfer: to={to}, amount={amount}")
logger.info(f"Transaction submitted: {tx_hash}")
Security Considerations¶
What abi-to-mcp Does NOT Protect Against¶
- Social engineering - User tricked into confirming bad transaction
- Malicious contracts - ABI doesn't reveal malicious code
- Private key theft - If environment is compromised
- Phishing addresses - Similar-looking addresses
What You Should Do¶
- Verify contracts - Only use verified contracts
- Check addresses - Double-check recipient addresses
- Start small - Test with small amounts first
- Use hardware wallets - For significant funds
- Monitor transactions - Watch for unexpected activity
Related¶
- Tool Types - Read vs write operations
- CLI Reference - Safety-related options
- Guides - Safe Claude integration