Skip to content

[spec] boa-mcp: MCP server for chain ops and compiling Vyper contracts #443

@lufa23

Description

@lufa23

boa-mcp: MCP Server for Vyper Development with Titanoboa

3 tools, local + fork + live chain, zero web3.py.


What This Is

An MCP server that lets AI agents compile Vyper contracts and interact with them via Titanoboa.

Design

  • Boa does everything. Titanoboa is the single dependency for all EVM interaction. One exception: eth_account.Account for loading private keys in network mode (boa transitive dep).
  • Three modes, same API. boa.load() / contract.function() work in all three. The server switches modes via environment setup:
    • Local (default): py-evm sandbox. No RPC, no key.
    • Fork: RPC URL set, no private key. Reads real chain state, writes stay local.
    • Network: RPC URL + private key. Real transactions.
  • Explicit broadcasting. Live chain operations require broadcast: true. No accidental real transactions.

Out of Scope (v1)

Hardware wallets, multi-account, event subscriptions, Etherscan ABI fetching, multiple compiler versions, tx replacement.

Tech Stack

  • Python 3.11+, mcp (stdio transport), titanoboa>=0.2.8

Configuration

Environment variables. No config file.

Variable Description
BOA_MCP_RPC_URL Ethereum RPC endpoint. Required for fork and network modes.
BOA_MCP_PRIVATE_KEY Private key for network mode. Hex string, with or without 0x.
BOA_MCP_EVM_VERSION Default EVM version. Default: cancun.

On startup, log mode to stderr. No stdout until MCP client connects.

Usage:

# Local mode (no config needed)
boa-mcp

# Fork mode
export BOA_MCP_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
boa-mcp

# Network mode (real transactions)
export BOA_MCP_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
export BOA_MCP_PRIVATE_KEY=0xYOUR_PRIVATE_KEY
boa-mcp

Tools

vyper_compile

Compile Vyper source to bytecode and ABI. Supports multi-file projects.

Inputs:

Parameter Required Description
sources Yes object - filename to source code map
main Yes string - main contract filename
evm_version No string - target EVM version

Outputs:

Field Type Description
bytecode string hex with 0x prefix
abi array JSON ABI (includes constructor entry if present)

Write all sources to a temp directory, call boa.load_partial() with compiler_args={'evm_version': v} if set.

Done when:

  • Single-file contract compiles
  • Multi-file contract with imports compiles
  • Compilation errors return file, line, column
  • ABI includes constructor entry when present

boa_deploy

Compile and deploy a Vyper contract to local, forked, or live EVM.

Inputs:

Parameter Required Description
sources Yes object - filename to source code map
main Yes string - main contract filename
args No array - constructor arguments
rpc_url No string - RPC URL. Falls back to BOA_MCP_RPC_URL.
broadcast No boolean - deploy to live chain. Default: false.

Outputs:

Field Type Description
address string EIP-55 checksummed
abi array contract ABI
gas_used integer gas consumed
mode string "local", "fork", or "network"
tx_hash string|null transaction hash (network mode only)

Mode logic: broadcast=true + RPC + key → boa.set_network_env() + add_account() (network). RPC without broadcast → boa.fork() (fork). Neither → local py-evm. Then boa.load() to compile and deploy.

Store deployed address -> contract object in memory for boa_interact.

Done when:

  • Deploys in local mode
  • Deploys in fork mode
  • Deploys in network mode (broadcast=true), returns tx_hash
  • broadcast=true without key or RPC returns error
  • Multi-file contracts deploy

boa_interact

Call any function on a deployed contract.

Inputs:

Parameter Required Description
address Yes string - contract address from boa_deploy
function Yes string - function name
args No array - function arguments
broadcast No boolean - real transaction on live chain. Default: false.

Outputs:

Field Type Description
result any decoded return value (JSON-serialized)
gas_used integer gas consumed
success boolean whether the call succeeded
error string|null revert reason if failed
tx_hash string|null transaction hash (network mode, state-changing only)

Look up contract by address, call getattr(contract, function_name)(*args). Serialize return values to JSON (integers as strings to prevent precision loss, bytes as hex, structs via _asdict()).

Done when:

  • View function returns result
  • State-changing function modifies state
  • Revert returns success: false with error message
  • broadcast=true sends real transaction with tx_hash
  • broadcast=true on view function works (free read)
  • broadcast=true without key returns error

Acceptance Tests

Test 1: Compile

{
  "sources": {"counter.vy": "#pragma version ~=0.4.0\ncount: public(uint256)\n@external\ndef increment():\n    self.count += 1"},
  "main": "counter.vy"
}

Returns bytecode and abi.

Test 2: Multi-File Compile

Compile a contract that imports an interface from a separate file.

Test 3: Deploy and Interact (Local)

  • boa_deploy with counter source, no RPC -> local deploy
  • boa_interact count -> '0'
  • boa_interact increment
  • boa_interact count -> '1'

Test 4: Deploy to Fork

  • boa_deploy with rpc_url pointing to Sepolia
  • Verify mode: "fork"

Test 5: Network Deploy + Interact

  • broadcast: true with Sepolia RPC + key -> deploys, returns tx_hash
  • broadcast: true on increment -> tx_hash
  • count (view, no broadcast) -> '1'

Test 6: Broadcast Safety

  • broadcast: true without key -> error
  • Default (no broadcast) with RPC -> fork mode, NOT live chain

Test 7: stdout Cleanliness

  • Every line on stdout is valid JSON-RPC (no stray prints from boa/vyper)

Future Expansion (Not v1)

Multi-account, hardware wallets, Etherscan ABI fetch, event log decoding, snapshot/revert (boa.env.anchor()), deployments persistence (boa.deployments.DeploymentsDB).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions