faucet-mcp
by Destiner
README.md
# faucet-mcp
MCP server that lets a coding agent request small dev funds (ETH/WETH/USDC/USDT) from a personal hot wallet, with caps and TOTP approval so a misbehaving agent can't drain it.
## Why
Agent work on smart wallets constantly needs small dev top-ups: fund a fresh smart account, seed a session key, test on a new chain. Public faucets are unreliable and rate-limited; topping up by hand is friction. This is a self-hosted faucet over MCP behind tight per-call and 24h budgets.
## Features
- Two MCP tools: `send` (transfer) and `status` (balances + remaining budget).
- Two-layer caps scoped per `(chain class, token pool)`:
- **Soft caps** — enforced unconditionally.
- **Hard caps** (10× soft) — also unconditional and *not* bypassable with a code.
- TOTP approval required for amounts above the soft cap; codes are single-use within a 90s replay window.
- Token pools: USDC + USDT share a `STABLE` window; ETH + WETH share `ETHWETH`.
- JIT WETH wrap on native-ETH chains — sends WETH even when the funder only holds ETH.
- Rolling 24h windows, pruned on each read.
- Chains: `ethereum`, `base`, `arbitrum`, `optimism`, `polygon`, `plasma` + their sepolia testnets and `plasma-testnet`.
## Caps (defaults)
| Chain class | Pool | Soft/call | Soft 24h | Hard/call | Hard 24h |
| ----------- | ------ | --------- | -------- | --------- | -------- |
| mainnet | stable | 0.10 | 1.0 | 1.0 | 10.0 |
| mainnet | eth | 0.00002 | 0.0005 | 0.0002 | 0.005 |
| testnet | stable | 2 | 20 | 20 | 200 |
| testnet | eth | 0.001 | 0.01 | 0.01 | 0.1 |
## Setup
```bash
bun install
bun run setup # generates a FAUCET_TOTP_SECRET + otpauth URI
cp .env.example .env # then fill FUNDING_PRIVATE_KEY and FAUCET_TOTP_SECRET
```
Add the printed `otpauth://` URI (or scan the QR) into Apple Passwords / 1Password as a verification-code entry. Fund the hot wallet with small dev amounts on the chains/tokens you expect to use.
Smoke-test the TOTP loop:
```bash
bun run bin/verify.ts <6-digit code from your authenticator>
```
## Configuration
| Variable | Required | Default | Description |
| --------------------- | -------- | ---------------------- | --- |
| `FUNDING_PRIVATE_KEY` | yes | — | 0x-prefixed hot wallet private key. |
| `FAUCET_TOTP_SECRET` | yes | — | Base32 TOTP secret (generated by `bun run setup`). |
| `FAUCET_STATE_FILE` | no | `~/.faucet/state.json` | Path for the rolling 24h send log + OTP replay cache. |
| `RPC_URL_<chainId>` | no | viem's public RPC | Override the RPC for a specific chain (e.g. `RPC_URL_1=https://...`). |
## Wire into Claude Code
`~/.claude.json` or project `.claude/settings.json`:
```json
{
"mcpServers": {
"faucet": {
"command": "bun",
"args": ["run", "--cwd", "/absolute/path/to/faucet-mcp", "src/server.ts"]
}
}
}
```
`bun run --cwd <dir>` makes Bun auto-load `.env` from the faucet directory, so the MCP can be invoked from any cwd.
## Tools
- **`send(amount, token, chain, recipient, code?)`** — execute a transfer. `amount` is a decimal string in human units (e.g. `"0.5"`), never wei. `code` is required only when the request exceeds the soft cap.
- **`status(chain?, token?)`** — funder balances, configured caps, used budget, remaining window. Use this to plan before calling `send`.
## License
MIT — see [LICENSE](./LICENSE).
For the full design notes, threat model, and limitations: see [AGENTS.md](./AGENTS.md).