chain-reader
chain-reader — 読み取り専用の Ethereum MCP サーバ
LLM に Ethereum を自然言語で読ませるための MCP サーバ。 秘密鍵を持たず、署名も送信もしない。すべての結果に「その答えがどこから来たか」が付く。
Tim Weingärtner (HSLU)『Ethereum & Smart Contracts』最終章「ブロックチェーンと AI」の図を、 そのまま動く形にした教材プロトタイプとして書いた。
LLM ← 自然言語(「このアドレスは何者?」)
↓
MCP ← src/server.js
↓ ← コード/構造化言語(ABI エンコード)
RPC ← src/rpc.js
↓
ブロックチェーン依存は @modelcontextprotocol/sdk と zod の 2 つだけ。
Keccak-256 も ABI エンコーダも自前で書いてある(後述の「なぜ自前で書いたか」)。
動かす
git clone <this repo> && cd chain-reader-mcp
npm ci --ignore-scripts
npm test # 単体 13 件(ネットワーク不要)
npm run smoke # 実チェーンに対して全ツールを 1 回ずつClaude Code に登録する。
claude mcp add chain-reader -- node "$PWD/src/server.js"このディレクトリで claude を起動するなら .mcp.json があるので登録は不要。
ただし初回だけ承認を求められる(claude mcp list に ⏸ Pending approval と出る)。
講義当日に慌てないよう、事前に一度起動して承認しておくこと。
Claude Desktop なら claude_desktop_config.json の mcpServers に同じ内容を書く。
その場合 args は絶対パスにする。
環境変数で対象ネットワークを切り替えられる。既定は mainnet。
変数 | 値 |
|
|
| 独自エンドポイント(指定するとネットワーク名より優先) |
いずれも API キー不要の公開エンドポイントを使う。local は anvil / hardhat node の
http://127.0.0.1:8545 を見る。
ツールと講義の対応
講義スライドそのものは別リポジトリ(私家版の日本語訳)にあるが、 節の名前だけ挙げておけば対応は追える。
ツール | 対応するスライド | 何が見えるか |
| ガスと取引手数料 / PoS | 基本手数料がブロックの混み具合で動くこと |
| 2種類のアカウント / Ethereum アドレス | コードの有無で EOA とコントラクトが分かれること |
| Etherscan でトランザクションを読む | 手数料 = ガス使用量 × 実効ガス価格 |
| ブロック |
|
| ABI / Solidity 入門 | セレクタが keccak256(署名) の先頭 4 バイトであること |
| ERC-20 / ERC-721 / クロークの引換札 | 名称も記号もコントラクトの自己申告であること |
| イベント駆動の UI |
|
| MCP を使うときの注意 | 鍵を持たない側にできることの限界 |
| ABI | ネットワークに触らずセレクタを計算する(板書用) |
| (論文側) | ハッシュのアンカリングで何が証明でき、何ができないか |
lecture_walkthrough プロンプトを選ぶと、1〜6 を順に辿る指示が入る。
講義でそのまま使える問いかけ
このネットワークはいま混んでいますか?
0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 は EOA ですか、コントラクトですか?
USDC の総供給量は? その数字は誰が保証していますか?
transfer(address,uint256) のセレクタはなぜ 0xa9059cbb になるのですか?
私のアドレスから 0.001 ETH を送る取引を組み立ててください最後の問いには、AI は組み立てた JSON を返すが送れない。 そこで「なぜ送れないのか」を説明させると、スライド「MCP を使うときの注意」の内容が AI 自身の口から出てくる。
設計上の 2 つの約束
1. 鍵を持たない
src/rpc.js の ALLOWED_METHODS は読み取り専用メソッドの明示的なホワイトリスト。
eth_sendRawTransaction / eth_sendTransaction / eth_sign はそこに無く、
呼ぼうとするとネットワークに出る前に落ちる(単体テストで固定してある)。
署名の実装も秘密鍵の読み込みもこのリポジトリには存在しない。 LLM がどう誘導されても、ここから資金は動かない。
prepare_unsigned_transaction は、この境界を「できないこと」ではなく
動く形で見せるためにある。nonce もガス見積もりも手数料も埋めた完成品を返し、
署名だけを人間に残す。講義スライドの
「MCP が安全に行えるのは、読み取り専用の呼び出しと、署名済みトランザクションの中継の 2 つに限られる」
がそのまま実装になっている。
2. 答えの出どころを捨てない
すべての結果に _provenance が付く。
"_provenance": {
"endpoint": "https://ethereum-rpc.publicnode.com",
"network": "mainnet (Ethereum Mainnet)",
"rpc_calls": ["eth_blockNumber (1309ms)", "eth_gasPrice (1416ms)", "eth_chainId (1769ms)", "eth_getBlockByNumber (1023ms)"],
"note": "これは単一の RPC エンドポイントの応答であり、独立に検証したものではない。"
}「ブロックチェーンだから正しい」で止めないための仕掛け。
LLM は数値を自信たっぷりに言い切る癖があるので、どの主張がどの層に立っているかを
結果自体に持たせる。サーバの instructions でも、チェーンが保証した事実と
誰かが申告した内容を区別して説明するよう指示している。
帰属できることと、検証できることは違う
このサーバの出力設計は、記録管理・デジタルアーカイブの文脈から来ている。 言えることとそれが本当であることの差を、ツールの出力に埋め込んである。
read_token の self_reported_note — name() が "USD Coin" を返したという事実は
チェーンが保証する。しかしそのコントラクトが本当に Circle のものかは保証しない。
同じ名前と記号のコントラクトは誰でもデプロイできる。
チェーンが保証するのは「この住所のコードがこう答えた」ことまでで、その主張の真偽ではない。
verify_anchor の what_this_does_not_prove — アンカリングが与えるのは
「いつ・誰が・何を主張したか」であって「その主張が正しいか」ではない。
誤った測定値のハッシュも、正しい測定値のハッシュと同じように刻める。
真正性 (authenticity) は真実性 (truth) ではない、という古文書学の区別がそのまま出る。
_provenance — 記録の品質とは、その来歴グラフの形のことである、という考え方の最小実装。
どのエンドポイントが、どの RPC 呼び出しで、何ミリ秒で答えたか。
PROV-O でいう prov:wasAttributedTo を誰にするかを、後から決められる状態にしておく。
署名された申告 / 公開情報との突合 / TEE アテステーション / 機関的な認証 と層を上げていくと 検証の強度は増すが、どこまで行っても「測定器そのもの」は検証できない。 このプロトタイプが実演しているのはその最下層 —— 帰属はできるが検証はできない領域。 だからこそ、どの層に立っている数値なのかを記録の側に残す。
なぜ Keccak も ABI も自前で書いたか
viem や ethers を入れれば 3 行で済む。あえて書いた理由が 2 つある。
講義の題材だから。 ABI が魔法のままでは「なぜ 4 バイトなのか」を説明できない。
src/keccak.jsとsrc/abi.jsは合わせて 300 行ほどで、受講者が読み切れる。依存を 2 つに抑えられるから。 サプライチェーンの面積が小さいほど、 3 年後に
npm ciして動く確率が上がる。
Node の crypto にある sha3-256 は NIST SHA-3 で、Ethereum の Keccak-256 とは
パディングが違う(0x06 と 0x01)ので流用できない。ここは実装するしかない。
対応範囲は address / uintN / intN / bool / bytesN / string / bytes と
その動的配列まで。タプルと入れ子の動的配列は扱わない。プロトタイプの範囲としては十分だが、
本番で任意のコントラクトを相手にするなら viem に置き換えること。
既知の限界
単一の RPC を信じている。 複数エンドポイントに同じ問いを投げて突き合わせれば 信頼の層が 1 つ上がる。実装していない
タプル型を扱えない。 Uniswap V3 の
slot0()のような戻り値はデコードできないread_eventsの走査範囲は既定で 200 ブロック。 公開エンドポイントは 広いeth_getLogsを拒否することがあるverify_anchorは部分文字列一致で探している。 アンカー用コントラクトの ABI が分かっているなら、正しく引数をデコードして照合すべきlocalネットワーク以外は公開エンドポイント依存。講義当日に落ちている可能性を考えて、anvil --fork-urlでローカルにフォークしておくと安全
ファイル構成
src/keccak.js Keccak-256(既知ベクタで固定)
src/abi.js ABI エンコード/デコード
src/rpc.js JSON-RPC クライアント + 読み取り専用ホワイトリスト
src/tools.js ツール 10 個の実体。MCP から独立していて単体で呼べる
src/server.js MCP サーバ(stdio)
test/unit.test.js ネットワーク不要の単体テスト
test/smoke.mjs 実チェーンに対する疎通確認
test/mcp-handshake.mjs MCP プロトコルの往復確認This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Free OpenAI-compatible inference with signed provenance receipts and 3 focused MCP tools.
Read-only MCP server for Robinhood Chain token discovery, research, and due diligence via GMGN.
MCP server for Blockscout
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/nakamura196/chain-reader-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server