MESC Overview

To get started quickly, see Quickstart.

To learn what MESC is, see What is MESC?.

To learn the motivation behind MESC, see Why MESC?.

To integrate MESC into an existing tool, see Integrating MESC.

To implement MESC in a new programming language, see Implementing MESC.

For information about existing MESC implementations, see:


The quickest way to use MESC is

  1. create a mesc.json config file
  2. set the MESC_PATH environment variable to the path of this file

Creating a config file

You can create a mesc.json by one of two ways:

  1. use the interactive MESC cli tool (install using cargo install mesc_cli and run mesc setup)
  2. modify the template below:
    "mesc_version": "MESC 1.0",
    "default_endpoint": "local_ethereum",
    "network_defaults": {
        "1": "local_ethereum"
    "network_names": {},
    "endpoints": {
        "local_ethereum": {
            "name": "local_ethereum",
            "url": "http://localhost:8545",
            "chain_id": "1",
            "endpoint_metadata": {}
    "profiles": {
        "xyz": {
            "name": "xyz",
            "default_endpoint": "local_ethereum",
            "network_defaults": {
                "1": "local_ethereum"
            "profile_metadata": {},
            "use_mesc": true
    "global_metadata": {}

The structure of the config must follow the MESC specification.

Installing the mesc cli on some linux distributions may require installing ssl libraries (e.g. sudo apt-get install pkg-config libssl-dev on ubunutu)

Setting environment variables

The typical way to set environment variables is in your shell configuration files: ~/.bashrc, ~.profile, and/or ~/.bash_profile. Including this line in those files will enable MESC:

export MESC_PATH=/path/to/your/mesc.json

You can avoid editing these files yourself by running the MESC setup tool (mesc setup) as specified above. It will give you the option to automatically edit your shell files.

What is MESC?

MESC (Multiple Endpoint Shared Configuration) is a standardized approach for configuring RPC endpoints. Using MESC, a user creates a single RPC config that is shared by all RPC tools on their system.

Design Goals

MESC has two main design goals:

  1. make it easy to share RPC configuration data across tools, languages, and environments
  2. make it easy to manage the configuration of a large number of RPC endpoints

MESC also has many secondary goals. It aims to be a solution that:

  • allows specifying multiple providers for multiple chains
  • allows selection of a default endpoint for each chain
  • allows using either environment variables or config files
  • is a single source of truth across multiple tools
  • is OS-agnostic, using no OS-specific features
  • is language-agnostic, using no language-specific features
  • is backwards compatible with previous solutions

MESC is opt-in

MESC is an opt-in standard: MESC is only enabled if a user has set a MESC-specific environment variable in their environment. This maximizes backward-compatiblity for existing tools that want to integrate MESC: If a user has not opted-in to MESC then simply fall back to the existing configuration method.


Between mainnet, testnets, chainforks, rollups, and alternative L1's, modern crypto tools must manage the configuration of multiple RPC endpoints. This configuration process is not standardized across tools.

The most common approach for configuring RPC endpoint information is the ETH_RPC_URL environment variable (dapptools, forge, heimdall, checkthechain). However, this is not a formal standard and many tools use other approaches. Furthermore, using ETH_RPC_URL can only specify a single provider for a single chain, and it cannot specify any provider metadata beyond the url.

Instead it would be desirable to have a solution that:

  • allows specifying multiple providers for multiple chains
  • allows selection of a default endpoint for each chain
  • allows using either environment variables or config files
  • is a single source of truth across multiple tools
  • is OS-agnostic, using no OS-specific features
  • is language-agnostic, using no language-specific features
  • is backwards compatible with previous solutions

MESC aims to satisfy all of these constraints.

As a developer, why should I integrate MESC into my tools?

  • Easier Onboarding. As soon as a user installs your tool, they will be able to use your tool with all of their configured networks. This eliminates one of the major setup steps for most RPC consuming tools.
  • Interoperability. If you maintain multiple tools, or your tool has components in multiple languages, MESC will create a single, shared source of truth across all of these tool components.
  • Safety and Robustness. MESC is thoroughly tested against a large number of config-related edge cases. In many cases it will be safer to user MESC than to create a configuration system from scratch.
  • Minimize maintenance burden. Leaning on MESC to handle configuration means there is one less thing you need to maintain. You can spend your time on other things.
  • Developer Features. If you need to develop or test your tool with multiple endpoints, the mesc CLI tools contains many useful developer features for managing endpoints.
  • Go config-free. In some cases, adopting MESC means that your tool no longer needs its own config. You can store your tool's config data in MESC's metadata and then just use MESC for all config IO.


Once the MESC specification reaches version 1.0, it will become ossified forever. This will maximize forward- and backward-compatibility between MESC integrations. Client libraries for reading or writing MESC configurations might continue development, but the underlying MESC specification will not change.

MESC currently has implementations for CLI, python, and rust. Implementations for go and typescript are coming soon. If there is another language you have interest in bringing MESC to an additional programming language or environment, feel free to share on the issue tracker.

Using MESC

  • To set MESC up, see Setup up MESC
  • To use different MESC settings for different tools, see Profiles
  • To use MESC for tracking miscellaneous metadata, see Metadata
  • To quickly override MESC settings without editing files, see Overrides

Setting up MESC

Basic setup

MESC becomes enabled by setting one or more environment variables.

In the simplest case, only one variable necessary: MESC_PATH is set to the path of a mesc.json config file.

As described in the quickstart, the mesc.json file is usually created using either 1) the interactive mesc setup command, or 2) copying from a starter template. The quickstart guide also describes how to set environment variables in your terminal shell.

Alternative setup without a mesc.json

Sometimes it is convenient to configure a system without editing any files (e.g. inside a container, or on a network drive, or in a low-privilege environment).

This can be accomplished with MESC by setting the MESC_ENV variable instead of the MESC_PATH variable. MESC_ENV should simply contain the JSON content of a MESC configuration.

If both MESC_PATH and MESC_ENV are set, you can select which one to use by setting MESC_MODE to either PATH or ENV. MESC_PATH takes precedence over MESC_ENV if MESC_MODE is not set.

Disabling MESC

MESC can be disabled by either 1) unsetting all MESC_* variables, or 2) setting MESC_MODE=DISABLED.

If MESC is disabled, the is_mesc_disabled() function will return false and all MESC querying functions will return an error (depending on language).


MESC also uses environment variables for overrides. See the overrides section for details.


Profiles are a way for each tool to customize its own MESC settings.

Profiles allow each tool to:

  • set its own default endpoint
  • set its own default endpoints for each network
  • override global metadata with its own metadata
  • avoid using MESC for that tool without disabling MESC globally

Profiles are an optional feature

If a MESC query does not specify a profile, the global configuration values are used.

If a MESC query specifies a profile, but that profile does not exist or does not specify the relevant information, MESC will fallback to using global configuration values.

Setting up a profile

The easiest way to create a profile is by using the interactive mesc cli tool, using the mesc setup subcommand.

Alternatively, you can manually add a new Profile to the profiles key inside your mesc.json. The following is a template Profile:

    "name": "PROFILE_NAME",
    "default_endpoint": null,
    "network_defaults": {},
    "profile_metadata": {},
    "use_mesc": true


In a MESC configuration, the global_metadata, profile_metadata, and endpoint_metadata fields allow for optional or idiosyncratic metadata to be stored alongside the core RPC data.

Tools using MESC can choose to ignore these fields.

Metadata fields

Contents of MESC metadata is only loosely specified. This is intentional to allow for some degree of future-proofing. It enables MESC to handle use-cases that are either unanticipated or highly-specific to a given tool.


Endpoint metadata

keyvalue typedescriptionexamples
rate_limit_rpsint or floatratelimit in requests per second250
rate_limit_cupsint or floatratelimit in CUPS1000
rate_limit_per_methodMapping[str, int or float]ratelimit in RPS for each method{"trace_block": 200}
api_keystrapi keya2798f237a2398rf7
jwt_secretstrjwt secret
hoststrname of provider host"llamanodes", "alchemy", "quicknode", "localhost"
ecosystemstrecosystem of chain, (e.g. relates mainnets to testnets)"ethereum", "polygon"
node_clientstrversioned node clienterigon/2.48.1/linux-amd64/go1.20.5 reth/v0.1.0-alpha.10-7b781eb60/x86_64-unknown-linux-gnu
namespacesSequence[str]RPC name spaces enabled for endpoint["eth", "trace, "debug"]
explorerstrblock explorer url
locationstrgeographic regionParis, France
cloud_regionstrcloud provider regionaws-us-east-1a
labelsSequence[str]tagsprivate_mempool, cache, archive, consensus_layer, execution_layer, validator, ephemeral

Global Metadata and Profile Metadata

keyvalue typedescriptionexamples
last_modified_bystrversioned tool used to create configurationmesc__1.0
last_modified_timeinttimestamp of config modification1700200462
creation_timeinttimestamp of config creation1700200462
api_keysMapping[str, str]API keys to RPC-related services{"etherscan": "abc123"}
groupsMapping[str, Sequence[str]]groupings of endpoints, mapping of group name to list of endpoint names{"load_balancer": ["alchemy_optimism", "quicknode_optimism"]}
concealboolwhether tool should avoid casually revealing private RPC url's unpromptedtrue


Overrides are a way to quickly modify a MESC configuration without needing to modify any files.

Each override is an environment variable that overrides a specific MESC configuration key.

Each override uses a syntax that is quick and easy to write by hand.

Example usage

The MESC_DEFAULT_ENDPOINT override changes the default endpoint.

Use in a script

Sometimes it might be useful tweak a MESC config for a shell script. This can be achieved by inserting the MESC overrides into the script:

#!/usr/bin/env bash

export MESC_DEFAULT_ENDPOINT=local_goerli

# of script that does RPC things

Prepending syntax

Adding VAR_NAME=VAR_VALUE before a cli command will set an environment variable for just that command.

So if before the default url is this:

mesc url

Adding the override before the command will change the default url:

MESC_DEFAULT_ENDPOINT=local_goerli mesc url
> localhost:8545

This syntax works for any cli program, not just the mesc cli tool.

List of overrides

Every type of information within a MESC configuration can be modified using overrides:

override variablevalue syntaxexample
MESC_DEFAULT_ENDPOINTurl, endpoint name, or network namelocalhost:9999
MESC_NETWORK_DEFAULTSspace-separated pairs of <chain_id>=<endpoint>5=alchemy_optimism 1=local_mainnet
MESC_NETWORK_NAMESspace-separated pairs of <network_name>=<chain_id>zora=7777777
MESC_ENDPOINTSspace-separated items of [<endpoint_name>[:<chain_id>]=]<url>alchemy_optimism= local_goerli:5=localhost:8545
MESC_PROFILESspace-separated pairs of <profile>.<key>[.<subkey]=<endpoint>foundry.default_endpoint=local_goerli foundry.network_defaults.5=alchemy_optimism
MESC_GLOBAL_METADATAJSON formatted global metadata{}
MESC_ENDPOINT_METADATAJSON mapping of {"endpoint_name": {<ENDPOINT_METADATA>}}{}

Querying MESC data

Each MESC implementation provides the same set of 7 functions for querying MESC data.

The behavior of these functions is the same across languages.

functionoutput typedescriptionexample call
is_mesc_enabled()boolreturn whether MESC is enabledis_mesc_enabled()
get_default_endpoint()Endpoint or Noneget default MESC endpointget_default_endpoint(profile='xyz')
get_endpoint_by_network()Endpoint or Noneget default endpoint for networkget_endpoint_by_network(5, profile='xyz')
get_endpoint_by_name()Endpoint or Noneget endpoint by nameget_endpoint_by_name('local_goerli')
get_endpoint_by_query()Endpoint or Noneget endpoint for user input queryget_endpoint_by_query(user_str, profile='xyz')
find_endpoints()Sequence[Endpoint]find endpoint that match input criteriafind_endpoints(chain_id=5)
get_global_metadata()Mapping[str, Any]get non-endpoint metadataget_global_metadata(profile='xyz')

The profile argument is optional for each function (it allows users to customize the settings for each tool, see Profiles for details).

Developer Resources

The MESC specification and its reference implementations are developed in the github repository.

  • For information about integrating MESC into an existing tool, see Integrating MESC.
  • For information about implementing a new MESC client, see Implementing MESC.
  • For information about running MESC tests, see MESC Test README.
  • For information about building these docs, see here.

Integrating MESC

MESC is designed to be easy to integrate into any existing tool in a backward-compatible manner.

MESC integration patterns can be designed so that they only affect users that have opted-in to MESC, while leaving other users unaffected.

Examples below are shown in python but work nearly identically for each language.

Workflow for getting the default RPC endpoint

Let's say that an existing tool named xyz currently gets its default RPC endpoint from an environment variable ETH_RPC_URL like so:

def get_default_rpc_url() -> str | None:
    return os.environ.get('ETH_RPC_URL')

MESC can be integrated using a few lines of code:

import mesc

def get_default_rpc_url() -> str | None:
    if mesc.is_mesc_enabled():
        endpoint = mesc.get_default_endpoint(profile='xyz')
        if endpoint is not None:
            return endpoint['url']
    return os.environ.get('ETH_RPC_URL')

This new function loads the MESC default endpoint for users that have opted-in to MESC. But it falls back to using ETH_RPC_URL if MESC is not enabled or MESC does not have a default endpoint set.

Using profile='xyz' is optional, but it allows users to set custom settings for the xyz tool instead of using the global settings.

Workflow for letting users select an RPC endpoint

Let's say that an existing tool named xyz currently lets users choose an RPC endpoint using an -r input argument. Users might use the tool in a similar manner to xyz -r or xyz -r localhost:8545.

If we add an extra parsing step for the user input, we can allow the user to select from any endpoint inside the MESC configuration by name, by network name, or by chain id. Using a pattern like this:

def get_endpoint_url(user_input: str) -> str:
    if mesc.is_mesc_enabled():
        endpoint = mesc.get_endpoint_by_query(user_input)
        if endpoint is not None:
            return endpoint['url']
    return user_input

# ...

url = get_endpoint_url(user_input)

Then users will be able to do any of the following

  • xyz -r localhost:8545 (use a url)
  • xyz -r llamanodes_goerli (use the endpoint named llamanodes_goerli)
  • xyz -r 5 (use the default endpoint for network with chain id 5)
  • xyz -r goerli (use the default endpoint for the network named goerli)

Implementing MESC

One of MESC's goals is to have an implementation for every programming language in the cryptocurrency ecosystem.

If you are creating a new MESC implementation, you may find the following useful:

  1. The main functionality that a MESC implementation provides is the core MESC interface. This is a set of functions for reading whatever MESC data a user has configured on their system.

  2. The ability to write MESC configurations is useful, but should be considered a secondary goal. Users already have the ability to create MESC configurations either by 1) editing JSON by hand, 2) editing JSON programmatically, or 3) using the interactive MESC cli tool.

  3. A MESC implementation's compliance to the specification can be checked using the language-agnostic MESC test suite. Passing the test suite means that an implementation is complete. More details on the test suite can be found here.

  4. It is desirable to use the same names, types, and behaviors across each MESC implementation. This increases interoperability and makes MESC easier to learn and use. However it is also desirable to obey the common conventions of each programming language. Each MESC implementation must find a balance between language-agnostic conventions vs language-specific conventions.

  5. If parts of the MESC specification are confusing or difficult to implement, it may be helpful to look at existing implementations. These currently exist for python and rust.

Implementation Checklist

  1. Implement the core MESC interface functions. These functions should have the same behavior in each implementation.
  2. Create an adapter to run the MESC test suite. Passing these tests means the implementation is fully compliant with the MESC specification.
  3. Add an entry to the script. This script creates a native language code file for mapping between network names and chain id's using the standard ethereum-lists/chains repository.

MESC Tests

A language-agnostic set of tests is used to check whether each MESC implementation is compliant with the MESC specification.


  1. Install pytest with pytest-xdist: pip install pytest pytest-xdist

  2. Go to the tests directory: cd $MESC_REPO/mesc/tests

  3. Run one of these commands:

run testspytest
run test in parallel mode (much faster)pytest -n auto
run tests in debug mode (helpful for debugging)pytest --pdb
run tests only tests that previously failedpytest --lf
run tests for specific adapters onlypytest --adapters adapters/python adapters/cli

By default, tests will run for all MESC implementations. If you do not have all of these implementations installed, you will need to use --adapters to select only the subset that that you have installed.


Each MESC implementation has an adapter that receives a test as input and prints the result as output. Adapters are located in the adapters directory.

To make a custom adapter:

  1. adapter should be a script that takes a JSON MescQuery as its single argument
  2. the adapter should run the query, and then print the result as JSON
  3. if the config loading or the query fails, simply print the word FAIL

The adapter should never crash upon failure, just print the word FAIL


  • adapters/ contains a test adapter for each MESC implementation
  • configuration file for pytest
  • generates all of the MESC test cases
  • packages MESC tests into form usable by pypi

MESC Libraries

MESC aims to have an implementation for every common language in the cryptocurrency ecosystem.

Implementations currently exist for:

If you want to create a MESC implementation for a new language, see implementing MESC.


This is a utility for creating and managing MESC RPC configurations.

Under the hood, this cli is implemented using the rust crate here.

The most important cli subcommands:

  1. mesc setup: create and modify MESC configs
  2. mesc ls: list endpoints
  3. mesc ping: ping endpoints
  4. mesc url: print endpoint url

View help for each subcommand by typing mesc [SUBCOMMAND] --help




Use one of the 3 options below. Check that mesc is properly installed and on your PATH by running mesc -h.

Install from

cargo install mesc_cli

Ensure that your cargo install path is on your cli path

Install from source

# install rust and cargo
curl --proto '=https' --tlsv1.2 -sSf | sh

# install mesc
git clone
cd mesc
cargo install --path rust/crates/cli

Example Usage

Quickly obtain RPC url's:

# curl the default network rpc url
curl $(mesc url) ...

# curl the default goerli url
curl $(mesc url goerli) ...

# curl an endpoint by name
curl $(mesc url local_goerli) ...

Print configuration data

# print all endpoints in table
mesc ls

# ping endpoints and collect metadata
mesc ping

# print default goerli endpoint data, human readable
mesc endpoint goerli

# print default goerli endpoint data, as json
mesc endpoint goerli --json


Show in terminal by typing mesc --help:

command line interface for creating, loading, and modifying MESC configuration data

Usage: mesc <COMMAND>

  setup     Create or modify config interactively
  import    Modify config by importing from file or other source
  set       Modify config by setting specific values
  ping      Ping endpoints and fetch metadata
  defaults  Print list of defaults
  endpoint  Print endpoint
  help      Print help
  ls        Print list of endpoints
  metadata  Print metadata
  status    Print status of configuration
  url       Print endpoint URL

  -h, --help     Print help
  -V, --version  Print version

Help topics: (print these with mesc help <TOPIC>)
  env       Environmental variables
  python    Python interface
  rust      Rust interface
  schema    Schemas of configs, endpoints, and profiles
  setup     How to set up MESC
  • If an endpoint is omitted, use the default endpoint
  • If a chain_id or network name is provided, use the default endpoint of network
  • Can use --profile PROFILENAME to use defaults of a particular profile

Go MESC Implementation

This is a reference implementation of the Go MESC standard.


To use this project within your Golang project, execute the following within your project:

go get

Example Usage

// TODO: pending doc


Refer to the function signatures defined in endpoints.go for the list of available functions.

Models are available in the model package.

Python MESC Implementation

This is a reference implementation of the Python MESC Standard

It has no external dependencies.


From PyPI

pip install mesc

From Source

git clone
cd mesc
pip install ./

Example Usage

from typing import Any, Mapping, Sequence
import mesc

# check whether mesc is enabled
enabled: bool = mesc.is_mesc_enabled()

# get the default endpoint
endpoint: Endpoint | None = mesc.get_default_endpoint()

# get the default endpoint of a network
endpoint: Endpoint | None = mesc.get_endpoint_by_network(5)

# get the default endpoint for a particular tool
endpoint: Endpoint | None = mesc.get_default_endpoint(profile='xyz_tool')

# get the default endpoint of a network for a particular tool
endpoint: Endpoint | None = mesc.get_endpoint_by_network(5, profile='xyz_tool')

# get an endpoint by name
endpoint: Endpoint | None = mesc.get_endpoint_by_name('local_goerli')

# parse a user-provided string into a matching endpoint
# (first try 1. endpoint name, then 2. chain id, then 3. network name)
endpoint: Endpoint | None = mesc.get_endpoint_by_query(user_str, profile='xyz_tool')

# find all endpoints matching given criteria
endpoints: Sequence[Endpoint] = mesc.find_endpoints(chain_id=5)

# get non-endpoint metadata
metadata: Mapping[str, Any] = mesc.get_global_metadata(profile='xyz_tool')


from typing import Any, MutableMapping, TypedDict, Literal, Sequence

class RpcConfig(TypedDict):
    mesc_version: str
    default_endpoint: str | None
    endpoints: MutableMapping[str, Endpoint]
    network_defaults: MutableMapping[str, str]
    network_names: MutableMapping[str, str]
    profiles: MutableMapping[str, Profile]
    global_metadata: MutableMapping[str, Any]

class Endpoint(TypedDict):
    name: str
    url: str
    chain_id: str | None
    endpoint_metadata: MutableMapping[str, Any]

class Profile(TypedDict):
    name: str
    default_endpoint: str | None
    network_defaults: MutableMapping[str, str]

Rust MESC Implementation

This is a reference rust implementation of the MESC standard.

The crates/mesc crate contains rust libraries for reading, writing, and validating MESC configurations.

The crates/mesc_cli crate is a tool for reading, writing, and validating MESC from the command line. See CLI for details.



Inside a cargo project: cargo add mesc

Inside a Cargo.toml: mesc = "0.2.0"

Example Usage

Basic data structures

fn main() {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RpcConfig {
    pub mesc_version: String,
    pub default_endpoint: Option<String>,
    pub endpoints: HashMap<String, Endpoint>,
    pub network_defaults: HashMap<ChainId, String>,
    pub network_names: HashMap<String, ChainId>,
    pub profiles: HashMap<String, Profile>,
    pub global_metadata: HashMap<String, serde_json::Value>,

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Endpoint {
    pub name: String,
    pub url: String,
    pub chain_id: Option<ChainId>,
    pub endpoint_metadata: HashMap<String, serde_json::Value>,

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Profile {
    pub name: String,
    pub default_endpoint: Option<String>,
    pub network_defaults: HashMap<ChainId, String>,
    pub profile_metadata: HashMap<String, serde_json::Value>,
    pub use_mesc: bool,

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash)]
pub struct ChainId(String);

Basic read functions

fn main() {
use mesc::{MescError, Endpoint};
use std::collections::HashMap;

type OptionalResult = Result<Option<Endpoint>, MescError>;
type MultiResult = Result<Vec<Endpoint>, MescError>;
type MetadataResult = Result<HashMap<String, serde_json::Value>, MescError>;

// check whether mesc is enabled
let enabled: bool = mesc::is_mesc_enabled();

// get the default endpoint
let endpoint: OptionalResult = mesc::get_default_endpoint(None);

// get the default endpoint of a network
let endpoint: OptionalResult = mesc::get_endpoint_by_network("5", None);

// get the default network for a particular tool
let chain_id: OptionalResult = mesc::get_default_endpoint(Some("xyz_tool"));

// get the default endpoint of a network for a particular tool
let endpoint: OptionalResult = mesc::get_endpoint_by_network("5", Some("xyz_tool"));

// get an endpoint by name
let endpoint: OptionalResult = mesc::get_endpoint_by_name("local_goerli");

// parse a user-provided string into a matching endpoint
// (first try 1. endpoint name, then 2. chain id, then 3. network name)
let user_str = "local_goerli";
let endpoint: OptionalResult = mesc::get_endpoint_by_query(user_str, Some("xyz_tool"));

// find all endpoints matching given criteria
let query = mesc::MultiEndpointQuery::new().chain_id("5").unwrap();
let endpoints: MultiResult = mesc::find_endpoints(query);

// get non-endpoint metadata
let metadata: MetadataResult  = mesc::get_global_metadata(Some("xyz_tool"));

Typescript MESC Implementation