web3-tools-mcp 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +115 -2
- package/README.md +96 -60
- package/dist/chain-meta.d.ts.map +1 -1
- package/dist/chain-meta.js +19 -8
- package/dist/chain-meta.js.map +1 -1
- package/dist/chains.d.ts +609 -65
- package/dist/chains.d.ts.map +1 -1
- package/dist/chains.js +89 -28
- package/dist/chains.js.map +1 -1
- package/dist/clear-signing.d.ts.map +1 -1
- package/dist/clear-signing.js +17 -6
- package/dist/clear-signing.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -4
- package/dist/client.js.map +1 -1
- package/dist/http-server.d.ts +1 -1
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +12 -5
- package/dist/http-server.js.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/kv-storage.d.ts +2 -1
- package/dist/kv-storage.d.ts.map +1 -1
- package/dist/kv-storage.js +5 -2
- package/dist/kv-storage.js.map +1 -1
- package/dist/oauth.d.ts +18 -4
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +62 -26
- package/dist/oauth.js.map +1 -1
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +7 -5
- package/dist/preview.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +16 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read/advanced.d.ts +25 -20
- package/dist/tools/read/advanced.d.ts.map +1 -1
- package/dist/tools/read/advanced.js +45 -33
- package/dist/tools/read/advanced.js.map +1 -1
- package/dist/tools/read/balance.d.ts +6 -6
- package/dist/tools/read/balance.d.ts.map +1 -1
- package/dist/tools/read/balance.js +17 -32
- package/dist/tools/read/balance.js.map +1 -1
- package/dist/tools/read/contract-info.d.ts +14 -12
- package/dist/tools/read/contract-info.d.ts.map +1 -1
- package/dist/tools/read/contract-info.js +53 -122
- package/dist/tools/read/contract-info.js.map +1 -1
- package/dist/tools/read/contract.d.ts +10 -10
- package/dist/tools/read/contract.d.ts.map +1 -1
- package/dist/tools/read/contract.js +2 -1
- package/dist/tools/read/contract.js.map +1 -1
- package/dist/tools/read/defi.d.ts +86 -0
- package/dist/tools/read/defi.d.ts.map +1 -0
- package/dist/tools/read/defi.js +210 -0
- package/dist/tools/read/defi.js.map +1 -0
- package/dist/tools/read/ens.d.ts +20 -40
- package/dist/tools/read/ens.d.ts.map +1 -1
- package/dist/tools/read/ens.js +25 -33
- package/dist/tools/read/ens.js.map +1 -1
- package/dist/tools/read/gas.d.ts +16 -16
- package/dist/tools/read/gas.d.ts.map +1 -1
- package/dist/tools/read/gas.js +13 -48
- package/dist/tools/read/gas.js.map +1 -1
- package/dist/tools/read/logs.d.ts +65 -4
- package/dist/tools/read/logs.d.ts.map +1 -1
- package/dist/tools/read/logs.js +96 -115
- package/dist/tools/read/logs.js.map +1 -1
- package/dist/tools/read/signatures.d.ts.map +1 -1
- package/dist/tools/read/signatures.js +4 -3
- package/dist/tools/read/signatures.js.map +1 -1
- package/dist/tools/read/signing.d.ts.map +1 -1
- package/dist/tools/read/signing.js +8 -2
- package/dist/tools/read/signing.js.map +1 -1
- package/dist/tools/sign/transactions.d.ts +16 -16
- package/dist/tools/sign/transactions.d.ts.map +1 -1
- package/dist/tools/sign/transactions.js +54 -9
- package/dist/tools/sign/transactions.js.map +1 -1
- package/dist/tools/sign/wallet.d.ts +4 -4
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +18 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +43 -2
- package/dist/utils.js.map +1 -1
- package/dist/wallet-client.d.ts +1 -0
- package/dist/wallet-client.d.ts.map +1 -1
- package/dist/wallet-client.js +18 -5
- package/dist/wallet-client.js.map +1 -1
- package/package.json +4 -7
- package/src/chain-meta.ts +16 -7
- package/src/chains.ts +101 -31
- package/src/clear-signing.ts +15 -9
- package/src/client.ts +2 -3
- package/src/http-server.ts +11 -6
- package/src/index.ts +5 -6
- package/src/kv-storage.ts +6 -3
- package/src/oauth.ts +93 -34
- package/src/preview.ts +7 -5
- package/src/tools/index.ts +14 -1
- package/src/tools/read/advanced.ts +63 -34
- package/src/tools/read/balance.ts +18 -33
- package/src/tools/read/contract-info.ts +64 -144
- package/src/tools/read/contract.ts +2 -1
- package/src/tools/read/defi.ts +287 -0
- package/src/tools/read/ens.ts +26 -33
- package/src/tools/read/gas.ts +13 -50
- package/src/tools/read/logs.ts +131 -143
- package/src/tools/read/signatures.ts +4 -4
- package/src/tools/read/signing.ts +8 -2
- package/src/tools/sign/transactions.ts +60 -9
- package/src/types.ts +0 -1
- package/src/utils.ts +47 -2
- package/src/wallet-client.ts +26 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,104 @@
|
|
|
2
2
|
|
|
3
3
|
Notable changes per release. Dates are release dates; unreleased work sits at the top.
|
|
4
4
|
|
|
5
|
-
## [3.
|
|
5
|
+
## [3.2.0] — 2026-10-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Monad, Robinhood Chain, Arc, Plasma, Ink, Mantle, Celo and HyperEVM (`hyperevm`).
|
|
10
|
+
`get_portfolio` skips Plasma and Mantle, where Alchemy has no token API, and
|
|
11
|
+
`get_contract_abi` cannot reach Ink, which Etherscan does not index.
|
|
12
|
+
- **`get_portfolio`** values a wallet across chains in one call: Alchemy finds the native and
|
|
13
|
+
ERC20 balances, DefiLlama prices them, largest first. Tokens DefiLlama cannot price are
|
|
14
|
+
left out and counted, which drops the spam airdrops that otherwise bury a wallet's real
|
|
15
|
+
holdings. Offered when `ALCHEMY_API_KEY` is set.
|
|
16
|
+
- **`get_token_prices`** prices up to 25 tokens on a chain in USD, now or at a past
|
|
17
|
+
timestamp, from DefiLlama's free API — the zero address for the native token. Tokens it
|
|
18
|
+
cannot price come back as unpriced.
|
|
19
|
+
- **`get_yield_pools`** lists DefiLlama's lending, staking and liquidity pools on the
|
|
20
|
+
supported chains, filtered by token, protocol, stablecoins and TVL, highest APY first.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- The signing page moved from `/` to `/sign/`, leaving the root free for a host's own page.
|
|
25
|
+
Signing links in the old `/#t=…` form no longer open it. `SIGN_PATH` is exported for hosts
|
|
26
|
+
building links.
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
|
|
30
|
+
- Infura: `INFURA_API_KEY` and `--infura-api-key`. Any other provider still works through
|
|
31
|
+
`CUSTOM_RPC`, chain by chain.
|
|
32
|
+
- Sonic. Alchemy serves no token API there, and its DeFi activity has largely moved on.
|
|
33
|
+
|
|
34
|
+
## [3.1.0] — 2026-10-01
|
|
35
|
+
|
|
36
|
+
Mostly fixes, several of them for security — worth taking on any hosted server.
|
|
37
|
+
|
|
38
|
+
From this release `web3-wallet-relay` shares the server's version number, jumping from 2.0.0
|
|
39
|
+
to 3.1.0. The two speak one protocol that carries no version of its own, so the package
|
|
40
|
+
version is what matches them: a relay hosted on its own runs at the version of the server
|
|
41
|
+
pointing at it. `web3-tools-mcp` still pins it exactly.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- A host's `Login` can name the `credential` it checks, binding every code and token issued
|
|
46
|
+
under it so that rotating the secret revokes them, and its `page()` is told which client
|
|
47
|
+
is asking and where the code will go.
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- `trace_transaction`'s `traceType` is `trace`, `prestate` or `stateDiff`. `vmTrace` is gone:
|
|
52
|
+
it ran the prestate tracer under another name, and `stateDiff` asked for a tracer no node
|
|
53
|
+
has. Traces too large for an agent's context are cut short and say so.
|
|
54
|
+
- `get_logs` returns at most 1,000 logs, with `truncated` and the `nextBlock` to continue
|
|
55
|
+
from. `toBlock` is inclusive on both paths, a list in `eventArgs` is an OR filter, and an
|
|
56
|
+
invalid `address` is an error rather than a filter silently dropped.
|
|
57
|
+
- `get_balance`, `read_contract` and `batch_resolve_ens_names` take at most 25 items.
|
|
58
|
+
- ENS tools take `mainnet`, `linea` or `base` — the chains that have a resolver — and the
|
|
59
|
+
no-op `universalResolver` parameter is gone.
|
|
60
|
+
- `render.yaml` is a single service: the relay runs inside the MCP server, as on Fly.
|
|
61
|
+
- Keeping OAuth state in Upstash needs per-field hash expiry (`HEXPIRE`, Redis ≥ 7.4), so
|
|
62
|
+
expired tokens no longer pile up. The file store prunes them on write.
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- `send_erc20_token` no longer assumes 18 decimals. It reads them on-chain and refuses a
|
|
67
|
+
`decimals` that disagrees; a token whose decimals cannot be read needs them passed.
|
|
68
|
+
- `write_contract` takes numeric arguments only as safe integers — pass a large uint or int
|
|
69
|
+
as a string — and coerces string arguments to their ABI types, as `read_contract` does.
|
|
70
|
+
- `sign_message` sends the browser the message hex-encoded, as the phone gets it, so both
|
|
71
|
+
sign the same bytes. The phone signs on a chain its session approved, not mainnet.
|
|
72
|
+
- Phone signing on `localhost` is refused up front: the phone has no route to that node. Use
|
|
73
|
+
the browser.
|
|
74
|
+
- The signing preview names each labelled address as `Name (0x…)`, shows token amounts with
|
|
75
|
+
the token's address, and gives an amount whose decimals are unknown as the raw integer,
|
|
76
|
+
marked so, rather than assuming 18. Its summary includes `to`.
|
|
77
|
+
- `get_logs` over Hypersync no longer drops the last block, reports a response cut short
|
|
78
|
+
by the server, or scans from genesis when `fromBlock` is omitted.
|
|
79
|
+
- `get_error_signature` hashes tuple parameters correctly.
|
|
80
|
+
- `get_gas_price` shows sub-gwei L2 prices instead of "0.00", and costs in the chain's own
|
|
81
|
+
native token.
|
|
82
|
+
- An invalid, expired or replayed OAuth code or refresh token answers `invalid_grant`, not a
|
|
83
|
+
500, so connectors re-authorise instead of retrying.
|
|
84
|
+
- Etherscan errors say why ("Invalid API Key", rate limit) rather than "NOTOK".
|
|
85
|
+
- `npx web3-wallet-relay` starts the relay.
|
|
86
|
+
|
|
87
|
+
### Security
|
|
88
|
+
|
|
89
|
+
- The provider API key no longer appears in tool output.
|
|
90
|
+
- `get_ens_avatar` returns the raw avatar record rather than fetching it server-side.
|
|
91
|
+
- Requests carry `expiresAt`, and the signing page cancels an approval that arrives after
|
|
92
|
+
the request timed out or was disconnected. A request that failed any way but an explicit
|
|
93
|
+
rejection now reports `outcome_unknown` — it may still have been broadcast — instead of
|
|
94
|
+
`rejected_or_failed`, so an agent checks before retrying rather than paying twice.
|
|
95
|
+
- The signing page is hardened against script injection and framing, with a strict CSP.
|
|
96
|
+
- OAuth refresh tokens expire, and rotating `MCP_TOKEN` revokes every token issued under
|
|
97
|
+
it — existing tokens predate the binding, so single-token deployments re-authorise once.
|
|
98
|
+
The login page names the client and where it redirects, and cannot be framed.
|
|
99
|
+
`MCP_TRUST_PROXY` lets rate limiting see client addresses behind a proxy.
|
|
100
|
+
- The hosted signer link is no longer written to the logs.
|
|
101
|
+
|
|
102
|
+
## [3.0.0] — 2026-09-26
|
|
6
103
|
|
|
7
104
|
The theme is multi-tenancy: one server can now carry several people without their wallets
|
|
8
105
|
meeting. Nothing turns that on by itself — a server still authenticates its deployment
|
|
@@ -36,6 +133,22 @@ handshake changed, so a mismatched pair cannot pair at all.
|
|
|
36
133
|
tools take `account` to choose between them, as does `disconnect_phone_wallet` — which
|
|
37
134
|
still clears everything when no account is named. With one wallet paired nothing changes,
|
|
38
135
|
and omitting `account` uses the most recently paired.
|
|
136
|
+
- **`simulate_bundle`** runs several transactions in order in one block, each on the state
|
|
137
|
+
the last left — approve then swap, deposit then borrow. Offered with an Alchemy key.
|
|
138
|
+
- **Signing requests outlive their call.** One not approved in time comes back with a
|
|
139
|
+
`requestId`, and `check_signing_request` collects it instead of a second prompt landing in
|
|
140
|
+
the wallet. A phone request returns as soon as the wallet has it, and both signers wait
|
|
141
|
+
for the receipt, reporting mined, reverted or still pending. The signing page shows every
|
|
142
|
+
request waiting, approvable in any order.
|
|
143
|
+
- A host assembling its own server can mount routes, hand a pairing made under a
|
|
144
|
+
placeholder to the address it proved, and draw the pairing QR as SVG (`qrSvg`).
|
|
145
|
+
- The server declares an icon, so a connector list shows more than a letter.
|
|
146
|
+
- `pair_phone_wallet` warns when the same wallet app is already paired, and reports a
|
|
147
|
+
pairing the wallet ended itself.
|
|
148
|
+
|
|
149
|
+
### Fixed
|
|
150
|
+
|
|
151
|
+
- An ERC-721 `Transfer` reads as a token id, not as an ERC-20 transfer of nothing.
|
|
39
152
|
|
|
40
153
|
### Security
|
|
41
154
|
|
|
@@ -177,7 +290,7 @@ sign it.
|
|
|
177
290
|
`call_contract_write` is now `write_contract` — the old pair gave no hint which one
|
|
178
291
|
spent gas. The names now match viem's, and a saved prompt naming the old ones will need
|
|
179
292
|
updating.
|
|
180
|
-
- Every supported chain now lives in one table ([mcp/src/chains.ts](mcp/src/chains.ts)). Adding a
|
|
293
|
+
- Every supported chain now lives in one table ([mcp/src/chains.ts](https://github.com/hajnalben/web3-tools-mcp/blob/main/mcp/src/chains.ts)). Adding a
|
|
181
294
|
chain is a single entry; the tool schemas, RPC selection, explorer links, Hypersync
|
|
182
295
|
routing and `--help` all derive from it.
|
|
183
296
|
- Tool errors surface the underlying failure rather than a re-wrapped message that hid it.
|
package/README.md
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="https://raw.githubusercontent.com/hajnalben/web3-tools-mcp/main/wallet-relay/public/icon.png" alt="Web3 Tools logo" width="112" height="112">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Web3 Tools MCP
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
**Everything onchain your agent needs — read, price, simulate, sign — with you holding the keys.** 🔐
|
|
8
|
+
|
|
9
|
+
35 MCP tools across 19 EVM networks, signing in *your own* wallet on your phone or in the browser.
|
|
10
|
+
Every transaction is decoded, named and simulated before it reaches your wallet.
|
|
9
11
|
|
|
10
12
|
[](https://www.npmjs.com/package/web3-tools-mcp)
|
|
11
13
|
[](https://www.npmjs.com/package/web3-tools-mcp)
|
|
12
14
|
[](https://nodejs.org)
|
|
13
|
-
[](LICENSE)
|
|
15
|
+
[](https://github.com/hajnalben/web3-tools-mcp/blob/main/LICENSE)
|
|
14
16
|
|
|
15
17
|
</div>
|
|
16
18
|
|
|
@@ -22,7 +24,7 @@ One entry in that client's MCP config:
|
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
Each app keeps that config somewhere different —
|
|
25
|
-
[the file paths are below ↓](#-setup). You need Node.js ≥ 20; `npx` fetches the rest.
|
|
27
|
+
[the file paths are below ↓](#-setup). You need Node.js ≥ 20.19; `npx` fetches the rest.
|
|
26
28
|
|
|
27
29
|
**ChatGPT, Grok** and **claude.ai** cannot launch anything locally — they only reach a URL.
|
|
28
30
|
[Host it once ↓](#-hosting) and add it as a connector, and there is nothing running on your
|
|
@@ -91,7 +93,7 @@ npx web3-tools-mcp --walletconnect-project-id YOUR_PROJECT_ID
|
|
|
91
93
|
| 💾 | **It sticks.** The session is stored on disk (or in Redis when hosted), so it survives restarts and redeploys. Pair once, not once a day |
|
|
92
94
|
| ✍️ | **You choose each time.** Every signing tool takes a required `signWith`, so the agent asks whether this one goes to your phone or the browser. A paired phone never quietly claims a request |
|
|
93
95
|
| 🧾 | **You still see the decode.** The full preview comes back in the tool response — your wallet shows its own summary, so read ours before you approve theirs |
|
|
94
|
-
| 🔌 | **Drop it any time.** `disconnect_phone_wallet`
|
|
96
|
+
| 🔌 | **Drop it any time.** `disconnect_phone_wallet` drops the wallet you name by `account`, or every session and pairing when you name none |
|
|
95
97
|
|
|
96
98
|
Works with **MetaMask · Rabby · Trust · Coinbase Wallet** — anything speaking WalletConnect v2.
|
|
97
99
|
|
|
@@ -123,28 +125,36 @@ returns the page URL any time you want to open it yourself.
|
|
|
123
125
|
## 🧰 Tools
|
|
124
126
|
|
|
125
127
|
<details>
|
|
126
|
-
<summary><b>
|
|
128
|
+
<summary><b>35 tools</b> — reads, writes, ENS, logs, tracing</summary>
|
|
127
129
|
|
|
128
130
|
### ✍️ Transactions & signing
|
|
129
131
|
| Tool | |
|
|
130
132
|
| --- | --- |
|
|
131
|
-
| `write_contract` | Send a transaction calling a state-changing function, via your wallet |
|
|
133
|
+
| `write_contract` | Send a transaction calling a state-changing function, via your wallet. Pass large integers as strings |
|
|
132
134
|
| `send_native_token` | Send ETH or a native token |
|
|
133
|
-
| `send_erc20_token` | Send an ERC-20 |
|
|
134
|
-
| `sign_message` | Sign a message |
|
|
135
|
-
| `
|
|
135
|
+
| `send_erc20_token` | Send an ERC-20, with decimals read on-chain — a `decimals` you pass must agree |
|
|
136
|
+
| `sign_message` | Sign a message — the same bytes on either signer |
|
|
137
|
+
| `check_signing_request` | Collect a request still awaiting approval, by its `requestId` |
|
|
138
|
+
| `wallet_status` | Every signer and whether it is ready — phone wallets and the browser page |
|
|
136
139
|
| `pair_phone_wallet` | Pair a phone over WalletConnect, returns a QR |
|
|
137
|
-
| `disconnect_phone_wallet` | Drop every
|
|
140
|
+
| `disconnect_phone_wallet` | Drop one paired wallet by `account`, or every session and pairing |
|
|
138
141
|
|
|
139
142
|
The four signing tools take a required `signWith` of `phone` or `browser`. There is no
|
|
140
143
|
default on purpose — the agent has to ask you, so a request never lands on a device you are
|
|
141
|
-
not holding.
|
|
144
|
+
not holding. On `localhost` only the browser can sign; a phone has no route to your node.
|
|
145
|
+
With several phones paired, the optional `account` picks one by address;
|
|
146
|
+
omitted, the most recently paired is used.
|
|
147
|
+
|
|
148
|
+
A request not approved within the call comes back with a `requestId` instead of hanging,
|
|
149
|
+
and `check_signing_request` collects it — asking again would put a second prompt in front
|
|
150
|
+
of your wallet.
|
|
142
151
|
|
|
143
152
|
### 📜 Contracts
|
|
144
153
|
| Tool | |
|
|
145
154
|
| --- | --- |
|
|
146
|
-
| `read_contract` | Read state via view/pure functions — batched, no wallet, no gas |
|
|
155
|
+
| `read_contract` | Read state via view/pure functions — batched (up to 25 calls), no wallet, no gas |
|
|
147
156
|
| `simulate_contract` | Simulate without broadcasting, with gas |
|
|
157
|
+
| `simulate_bundle` | Simulate several transactions in order, each on the state the last left — approve → swap. Needs `ALCHEMY_API_KEY` |
|
|
148
158
|
| `get_contract_abi` | ABI, with proxy detection and verification status |
|
|
149
159
|
| `get_contract_source_code` | Verified source, proxies included |
|
|
150
160
|
| `get_contract_source_file` | One file out of the cached source |
|
|
@@ -157,14 +167,23 @@ not holding.
|
|
|
157
167
|
### ⛓️ Chain data
|
|
158
168
|
| Tool | |
|
|
159
169
|
| --- | --- |
|
|
160
|
-
| `get_balance` | Native or ERC-20 balances — batched |
|
|
161
|
-
| `get_logs` | Query and decode events,
|
|
170
|
+
| `get_balance` | Native or ERC-20 balances — batched, up to 25 queries |
|
|
171
|
+
| `get_logs` | Query and decode events; Hypersync when the RPC refuses the range. At most 1,000 logs — past that it returns `truncated` and the `nextBlock` to continue from. A list in `eventArgs` matches any of its values |
|
|
162
172
|
| `get_block_info` | Block data |
|
|
163
173
|
| `get_storage_at` | Storage slots, with type decoding |
|
|
164
174
|
| `get_gas_price` | Current gas — legacy and EIP-1559 |
|
|
165
175
|
| `estimate_gas` | Gas for any transaction |
|
|
166
|
-
| `trace_transaction` | Call tree,
|
|
167
|
-
| `debug_call` | Trace a call without broadcasting,
|
|
176
|
+
| `trace_transaction` | Call tree, prestate or state diff |
|
|
177
|
+
| `debug_call` | Trace a call without broadcasting, through a node at `ANVIL_RPC_URL` |
|
|
178
|
+
| `get_portfolio` | A wallet's native and ERC-20 holdings across chains, valued in USD; tokens DefiLlama cannot price — spam, mostly — are left out. Needs `ALCHEMY_API_KEY` |
|
|
179
|
+
| `get_token_prices` | USD prices from DefiLlama, now or at a past timestamp, up to 25 tokens; the zero address prices the native token |
|
|
180
|
+
| `get_yield_pools` | DefiLlama yield pools by chain, token and protocol, highest APY first |
|
|
181
|
+
|
|
182
|
+
Tracing prefers an RPC that exposes `debug_*`, and otherwise uses a node you already run at
|
|
183
|
+
`ANVIL_RPC_URL` (default `127.0.0.1:8545`, e.g. `anvil --fork-url …`) — nothing is started
|
|
184
|
+
for you. Replaying a transaction older than the fork re-forks that node, clearing its state,
|
|
185
|
+
so it happens only with `ANVIL_ALLOW_RESET=1`. A hosted server has no default and traces only
|
|
186
|
+
through an explicit `ANVIL_RPC_URL`.
|
|
168
187
|
|
|
169
188
|
### 🏷️ ENS
|
|
170
189
|
| Tool | |
|
|
@@ -172,13 +191,15 @@ not holding.
|
|
|
172
191
|
| `resolve_ens_name` | Name → address |
|
|
173
192
|
| `reverse_resolve_ens` | Address → name |
|
|
174
193
|
| `get_ens_text_record` | Text records |
|
|
175
|
-
| `get_ens_avatar` |
|
|
176
|
-
| `batch_resolve_ens_names` |
|
|
194
|
+
| `get_ens_avatar` | The raw avatar record — URL, data URI or NFT reference, not fetched |
|
|
195
|
+
| `batch_resolve_ens_names` | Up to 25 names at once |
|
|
196
|
+
|
|
197
|
+
ENS tools take `mainnet` (ENS), `linea` (Linea Names) or `base` (Basenames).
|
|
177
198
|
|
|
178
199
|
</details>
|
|
179
200
|
|
|
180
201
|
<details>
|
|
181
|
-
<summary>🌐 <b>
|
|
202
|
+
<summary>🌐 <b>20 networks</b></summary>
|
|
182
203
|
|
|
183
204
|
| Network | Chain ID | Hypersync |
|
|
184
205
|
| --- | --- | --- |
|
|
@@ -188,15 +209,22 @@ not holding.
|
|
|
188
209
|
| Base | 8453 | ✅ |
|
|
189
210
|
| BNB Chain | 56 | ✅ |
|
|
190
211
|
| Gnosis | 100 | ✅ |
|
|
191
|
-
| Sonic | 146 | ✅ |
|
|
192
212
|
| Optimism | 10 | ✅ |
|
|
193
213
|
| Polygon | 137 | ✅ |
|
|
194
214
|
| zkSync Era | 324 | ✅ |
|
|
195
215
|
| Linea | 59144 | ✅ |
|
|
196
216
|
| Unichain | 130 | ✅ |
|
|
217
|
+
| Monad | 143 | ✅ |
|
|
218
|
+
| Robinhood Chain | 4663 | ✅ |
|
|
219
|
+
| Arc | 5042 | ✅ |
|
|
220
|
+
| Plasma | 9745 | ✅ |
|
|
221
|
+
| Ink | 57073 | ✅ |
|
|
222
|
+
| Mantle | 5000 | ✅ |
|
|
223
|
+
| Celo | 42220 | ✅ |
|
|
224
|
+
| HyperEVM | 999 | ✅ |
|
|
197
225
|
| Localhost | 1337 | ❌ |
|
|
198
226
|
|
|
199
|
-
Adding one is a single entry in [`mcp/src/chains.ts`](mcp/src/chains.ts).
|
|
227
|
+
Adding one is a single entry in [`mcp/src/chains.ts`](https://github.com/hajnalben/web3-tools-mcp/blob/main/mcp/src/chains.ts).
|
|
200
228
|
|
|
201
229
|
</details>
|
|
202
230
|
|
|
@@ -289,17 +317,17 @@ What you lose is reach, not stability — so here is the honest version:
|
|
|
289
317
|
| Key | Free from | Without it |
|
|
290
318
|
| --- | --- | --- |
|
|
291
319
|
| `WALLETCONNECT_PROJECT_ID` | [walletconnect](https://dashboard.walletconnect.com) | 📱 **No phone signing at all.** Browser wallet only, so a hosted instance has no way to sign |
|
|
292
|
-
| `ALCHEMY_API_KEY`<br>or `
|
|
320
|
+
| `ALCHEMY_API_KEY`<br>or `CUSTOM_RPC` | [alchemy](https://alchemy.com) | **No historical state.** Public endpoints answer `403 Archive requests require a personal token`, which takes out balances, storage and calls at a past block, event ranges, and `trace_transaction` |
|
|
293
321
|
| `ETHERSCAN_API_KEY` | [etherscan](https://etherscan.io/apis) | `get_contract_abi`, `get_contract_source_code` and `get_contract_source_file` refuse. Signing previews still decode — they try Sourcify first, then recover the ABI from bytecode |
|
|
294
|
-
| `HYPERSYNC_API_KEY` | [envio](https://envio.dev) |
|
|
322
|
+
| `HYPERSYNC_API_KEY` | [envio](https://envio.dev) | `get_logs` asks the RPC first and falls back to Hypersync when the RPC refuses the range. Without a token that fallback is gone, so wide or past ranges depend on your provider's limits |
|
|
295
323
|
|
|
296
324
|
So with nothing configured you get current balances, contract reads, gas, ENS, simulation
|
|
297
325
|
and browser signing. Add `WALLETCONNECT_PROJECT_ID` for your phone and one provider key for
|
|
298
326
|
history, and everything above lights up.
|
|
299
327
|
|
|
300
328
|
Every key has a matching flag (`--etherscan-api-key`, …), RPC selection falls back
|
|
301
|
-
Alchemy →
|
|
302
|
-
`npx web3-tools-mcp --help` lists them; [.env.example](.env.example) documents each one.
|
|
329
|
+
Alchemy → public, and `CUSTOM_RPC` overrides both, chain by chain.
|
|
330
|
+
`npx web3-tools-mcp --help` lists them; [.env.example](https://github.com/hajnalben/web3-tools-mcp/blob/main/.env.example) documents each one.
|
|
303
331
|
|
|
304
332
|
```bash
|
|
305
333
|
npx web3-tools-mcp --etherscan-api-key KEY --walletconnect-project-id ID
|
|
@@ -319,26 +347,34 @@ transaction at your phone.
|
|
|
319
347
|
<a href="https://fly.io/docs/launch/"><img src="https://img.shields.io/badge/Deploy%20on-Fly.io-8b5cf6?style=for-the-badge&logo=flydotio&logoColor=white" alt="Deploy on Fly.io" height="32"></a>
|
|
320
348
|
|
|
321
349
|
**Render** reads `render.yaml`, generates `MCP_TOKEN` and asks for `WALLETCONNECT_PROJECT_ID`
|
|
322
|
-
—
|
|
350
|
+
— one click, on a paid `starter` instance with a 1 GB disk so it stays awake and keeps the
|
|
351
|
+
pairing. `MCP_PUBLIC_URL` defaults to the service's `onrender.com` address; set it only for a
|
|
352
|
+
custom domain.
|
|
323
353
|
|
|
324
354
|
**Fly** ships no deploy button, so it is four commands. Worth it for a personal instance:
|
|
325
355
|
volumes come with any plan, so the pairing survives redeploys without paying for a disk.
|
|
326
356
|
|
|
327
357
|
```bash
|
|
328
|
-
fly launch --no-deploy --copy-config
|
|
358
|
+
fly launch --no-deploy --copy-config # then set MCP_PUBLIC_URL in fly.toml to https://<app>.fly.dev
|
|
329
359
|
fly volumes create data --size 1
|
|
330
360
|
fly secrets set MCP_TOKEN=$(openssl rand -hex 24) WALLETCONNECT_PROJECT_ID=<id>
|
|
331
361
|
fly deploy
|
|
332
362
|
```
|
|
333
363
|
|
|
364
|
+
Without the right `MCP_PUBLIC_URL` the OAuth metadata and the wallet links point somewhere
|
|
365
|
+
clients cannot reach. Both `fly.toml` and `render.yaml` set `MCP_TRUST_PROXY=1`, so rate
|
|
366
|
+
limiting sees client addresses rather than the platform's proxy.
|
|
367
|
+
|
|
334
368
|
<details>
|
|
335
369
|
<summary>🐳 <b>Docker, and connecting a client</b></summary>
|
|
336
370
|
|
|
337
371
|
There is a plain `Dockerfile` too, so Railway, Cloud Run or your own box work the same way:
|
|
338
372
|
|
|
339
373
|
```bash
|
|
374
|
+
docker build -t web3-tools-mcp .
|
|
340
375
|
docker run -p 8080:8080 \
|
|
341
376
|
-e MCP_TOKEN=<long random string> \
|
|
377
|
+
-e MCP_PUBLIC_URL=https://your-host \
|
|
342
378
|
-e WALLETCONNECT_PROJECT_ID=<id> \
|
|
343
379
|
web3-tools-mcp
|
|
344
380
|
```
|
|
@@ -352,54 +388,54 @@ claude mcp add --transport http web3-tools https://your-host/mcp \
|
|
|
352
388
|
|
|
353
389
|
Clients that cannot send a header — claude.ai connectors, and the browser and phone apps —
|
|
354
390
|
use OAuth, which the server implements against the same credential: it shows a page asking
|
|
355
|
-
for `MCP_TOKEN` and issues a token once you paste it. No identity provider, no accounts.
|
|
356
|
-
`MCP_PUBLIC_URL` to the address clients reach you on, since OAuth metadata must advertise it.
|
|
391
|
+
for `MCP_TOKEN` and issues a token once you paste it. No identity provider, no accounts.
|
|
357
392
|
|
|
358
393
|
> 🔐 **`MCP_TOKEN` is mandatory** and the server refuses to start without it. Anyone who
|
|
359
394
|
> reaches `/mcp` while your phone is paired can push signing prompts at it. You would still
|
|
360
395
|
> approve each one, but that is a phishing surface, not a feature.
|
|
361
396
|
|
|
397
|
+
| Variable | |
|
|
398
|
+
| --- | --- |
|
|
399
|
+
| `MCP_HTTP_PORT` | Serve over HTTP on this port instead of stdio. Also switches on the hosted guards: no `localhost` chain, no loopback tracing node |
|
|
400
|
+
| `MCP_HTTP_HOST` | Bind address, default `0.0.0.0` |
|
|
401
|
+
| `MCP_TOKEN` | The one credential, as a bearer header or through OAuth. Changing it revokes every OAuth token issued against the old one |
|
|
402
|
+
| `MCP_PUBLIC_URL` | The address clients reach you on — advertised in OAuth metadata and used for wallet links |
|
|
403
|
+
| `MCP_TRUST_PROXY` | Reverse-proxy hops to trust for client IPs in rate limiting. `1` behind Fly or Render; off by default |
|
|
404
|
+
| `MCP_HOSTED` | The hosted guards without `MCP_HTTP_PORT`, for a host assembling its own server from the library |
|
|
405
|
+
| `XDG_CONFIG_HOME` | Where pairings, OAuth tokens and the relay token are stored, under `web3-tools-mcp/` |
|
|
406
|
+
| `UPSTASH_REDIS_REST_URL`<br>`UPSTASH_REDIS_REST_TOKEN` | Keep sessions and tokens in Redis instead. Needs per-field hash expiry (`HEXPIRE`: Upstash, or Redis ≥ 7.4) |
|
|
407
|
+
| `WALLET_TOKEN` | Relay secret. Unset, a hosted server mints one per boot |
|
|
408
|
+
| `WALLET_SERVER_URL` | A separately hosted relay, below |
|
|
409
|
+
| `ANVIL_RPC_URL`<br>`ANVIL_ALLOW_RESET` | Tracing node, and whether it may be re-forked — see Tools |
|
|
410
|
+
|
|
362
411
|
Two things decide whether the pairing survives:
|
|
363
412
|
|
|
364
413
|
- ⏰ **Keep the instance awake.** Sleeping drops the WalletConnect socket, so the first
|
|
365
414
|
transaction after an idle period waits for the host to wake — about a minute on a free tier.
|
|
366
415
|
- 💾 **Give the session somewhere durable.** Most hosts have an ephemeral filesystem, so a
|
|
367
416
|
plain file is lost on redeploy and you rescan the QR. Attach a volume and point
|
|
368
|
-
`XDG_CONFIG_HOME` at it (what `render.yaml`
|
|
369
|
-
`UPSTASH_REDIS_REST_TOKEN` ([Upstash](https://upstash.com)
|
|
417
|
+
`XDG_CONFIG_HOME` at it (what `render.yaml` and `fly.toml` do), or set
|
|
418
|
+
`UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` ([Upstash](https://upstash.com)
|
|
419
|
+
has a free plan).
|
|
370
420
|
|
|
371
421
|
Render's free Key Value instance is not an option here — no persistence, so the pairing
|
|
372
422
|
disappears at its next maintenance.
|
|
373
423
|
|
|
374
424
|
Both signers work on a hosted instance. The signing page is served from the same port as
|
|
375
|
-
`/mcp`, so `https://your-host/#t=<relay token>` is your wallet page — no second service to
|
|
376
|
-
deploy. Ask the agent for `wallet_status` to get the link; it is behind `MCP_TOKEN
|
|
377
|
-
relay
|
|
378
|
-
redeploy.
|
|
425
|
+
`/mcp`, so `https://your-host/sign/#t=<relay token>` is your wallet page — no second service to
|
|
426
|
+
deploy. The root is left free for a page of your own. Ask the agent for `wallet_status` to get the link; it is behind `MCP_TOKEN` and never written to the logs. Unless
|
|
427
|
+
you set `WALLET_TOKEN`, the relay secret is minted fresh each boot rather than stored, so the
|
|
428
|
+
URL changes when you redeploy.
|
|
379
429
|
|
|
380
430
|
</details>
|
|
381
431
|
|
|
382
432
|
<details>
|
|
383
433
|
<summary>🖇️ <b>Host the signing page</b> separately</summary>
|
|
384
434
|
|
|
385
|
-
The relay is its own package, [`wallet-relay
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
```bash
|
|
390
|
-
WALLET_TOKEN=<long random string> \
|
|
391
|
-
WALLET_PUBLIC_URL=https://wallet.example.com \
|
|
392
|
-
npx web3-wallet-relay
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
Then point the server at it with `WALLET_SERVER_URL` and the same `WALLET_TOKEN`.
|
|
396
|
-
`render.yaml` deploys it as-is. Anyone holding `WALLET_TOKEN` can send your browser a
|
|
397
|
-
transaction to sign — treat it like a password, and always serve over HTTPS.
|
|
398
|
-
|
|
399
|
-
The pairing link the server prints carries a different token, derived from that one, which
|
|
400
|
-
can only answer signing requests and not make them. Sharing a pairing link is therefore not
|
|
401
|
-
the same as sharing `WALLET_TOKEN` — but it still lets whoever has it act as your wallet
|
|
402
|
-
page, so a signing request can reach them instead of you.
|
|
435
|
+
The relay is its own package, [`web3-wallet-relay`](https://github.com/hajnalben/web3-tools-mcp/tree/main/wallet-relay),
|
|
436
|
+
and runs anywhere that keeps a Node process alive and supports WebSockets. Its README covers
|
|
437
|
+
hosting it; run it at this server's version, then point the server at it with
|
|
438
|
+
`WALLET_SERVER_URL` and the same `WALLET_TOKEN`.
|
|
403
439
|
|
|
404
440
|
Locally nothing changes: the server embeds the same relay.
|
|
405
441
|
|
|
@@ -421,11 +457,11 @@ npm run typecheck
|
|
|
421
457
|
npm test
|
|
422
458
|
```
|
|
423
459
|
|
|
424
|
-
Two workspaces: [`mcp/`](mcp) is this server, [`wallet-relay/`](wallet-relay) is the signing
|
|
425
|
-
page. [CONTRIBUTING.md](CONTRIBUTING.md) covers running a checkout against a client, the
|
|
426
|
-
layout, and how to add a chain or a tool. Releases: [CHANGELOG.md](CHANGELOG.md).
|
|
460
|
+
Two workspaces: [`mcp/`](https://github.com/hajnalben/web3-tools-mcp/tree/main/mcp) is this server, [`wallet-relay/`](https://github.com/hajnalben/web3-tools-mcp/tree/main/wallet-relay) is the signing
|
|
461
|
+
page. [CONTRIBUTING.md](https://github.com/hajnalben/web3-tools-mcp/blob/main/CONTRIBUTING.md) covers running a checkout against a client, the
|
|
462
|
+
layout, and how to add a chain or a tool. Releases: [CHANGELOG.md](https://github.com/hajnalben/web3-tools-mcp/blob/main/CHANGELOG.md).
|
|
427
463
|
|
|
428
|
-
Requires Node.js ≥ 20.
|
|
464
|
+
Requires Node.js ≥ 20.19.
|
|
429
465
|
|
|
430
466
|
## 📄 License
|
|
431
467
|
|
package/dist/chain-meta.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain-meta.d.ts","sourceRoot":"","sources":["../src/chain-meta.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,KAAK,GAAG,EAA8B,MAAM,MAAM,CAAA;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"chain-meta.d.ts","sourceRoot":"","sources":["../src/chain-meta.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,KAAK,GAAG,EAA8B,MAAM,MAAM,CAAA;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAiB3C,wBAAsB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM;SAP1B,GAAG;YAAU,UAAU,GAAG,SAAS;YAAU,MAAM;WAAS,MAAM;GAwCtG;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM;aAxCrB,MAAM;eAAa,MAAM;GA6DlE;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqBjG;AAED,wEAAwE;AACxE,eAAO,MAAM,WAAW,QAAoB,CAAA;AAC5C,eAAO,MAAM,mBAAmB,QAAmB,CAAA"}
|
package/dist/chain-meta.js
CHANGED
|
@@ -6,13 +6,18 @@
|
|
|
6
6
|
import { whatsabi } from '@shazow/whatsabi';
|
|
7
7
|
import { parseAbiItem } from 'viem';
|
|
8
8
|
import { getClientManager } from './client.js';
|
|
9
|
+
import { TtlCache } from './utils.js';
|
|
9
10
|
const ERC20_META_ABI = [
|
|
10
11
|
parseAbiItem('function decimals() view returns (uint8)'),
|
|
11
12
|
parseAbiItem('function symbol() view returns (string)')
|
|
12
13
|
];
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
14
|
+
// An hour covers a session's repeats while still noticing an upgraded proxy.
|
|
15
|
+
const TTL_MS = 60 * 60 * 1000;
|
|
16
|
+
const abiCache = new TtlCache(200, TTL_MS);
|
|
17
|
+
const labelCache = new TtlCache(1000, TTL_MS);
|
|
18
|
+
const metaCache = new TtlCache(1000, TTL_MS);
|
|
19
|
+
/** A local node is reset and redeployed at will, so nothing learned from it stays true. */
|
|
20
|
+
const cacheable = (chain) => chain !== 'localhost';
|
|
16
21
|
export async function loadAbi(chain, address) {
|
|
17
22
|
const key = `${chain}:${address.toLowerCase()}`;
|
|
18
23
|
const cached = abiCache.get(key);
|
|
@@ -41,7 +46,8 @@ export async function loadAbi(chain, address) {
|
|
|
41
46
|
proxy: result.address !== address ? result.address : undefined,
|
|
42
47
|
name: result.contractResult?.name ?? undefined
|
|
43
48
|
};
|
|
44
|
-
|
|
49
|
+
if (cacheable(chain))
|
|
50
|
+
abiCache.set(key, loaded);
|
|
45
51
|
return loaded;
|
|
46
52
|
}
|
|
47
53
|
export async function tokenMeta(chain, token) {
|
|
@@ -61,7 +67,9 @@ export async function tokenMeta(chain, token) {
|
|
|
61
67
|
decimals: decimals.status === 'success' ? Number(decimals.result) : undefined,
|
|
62
68
|
symbol: symbol.status === 'success' ? symbol.result : undefined
|
|
63
69
|
};
|
|
64
|
-
|
|
70
|
+
// A failed call may be a flaky RPC rather than a token without that method.
|
|
71
|
+
if (cacheable(chain) && decimals.status === 'success' && symbol.status === 'success')
|
|
72
|
+
metaCache.set(key, meta);
|
|
65
73
|
return meta;
|
|
66
74
|
}
|
|
67
75
|
/**
|
|
@@ -71,8 +79,9 @@ export async function tokenMeta(chain, token) {
|
|
|
71
79
|
*/
|
|
72
80
|
export async function addressLabel(chain, address) {
|
|
73
81
|
const key = `${chain}:${address.toLowerCase()}`;
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
const cached = labelCache.get(key);
|
|
83
|
+
if (cached)
|
|
84
|
+
return cached.label;
|
|
76
85
|
let label;
|
|
77
86
|
try {
|
|
78
87
|
const code = await getClientManager()
|
|
@@ -86,8 +95,10 @@ export async function addressLabel(chain, address) {
|
|
|
86
95
|
}
|
|
87
96
|
catch {
|
|
88
97
|
// Unknown address — show it bare rather than failing the preview.
|
|
98
|
+
return undefined;
|
|
89
99
|
}
|
|
90
|
-
|
|
100
|
+
if (cacheable(chain))
|
|
101
|
+
labelCache.set(key, { label });
|
|
91
102
|
return label;
|
|
92
103
|
}
|
|
93
104
|
/** uint256 max and anything near it means "infinite" in an approval. */
|
package/dist/chain-meta.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain-meta.js","sourceRoot":"","sources":["../src/chain-meta.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAA0B,YAAY,EAAE,MAAM,MAAM,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"chain-meta.js","sourceRoot":"","sources":["../src/chain-meta.ts"],"names":[],"mappings":"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"}
|