Read Before Sign
README.md
# Read Before Sign
**Give your AI agent a way to read wallet requests before anyone signs them.**

An MCP server and CLI that turns a signing request into a short plain-English briefing: what it asks for, what needs your attention, and what could not be verified. It runs offline. No RPC, no wallet, no API key, no verdict.
```
$ read-before-sign explain permit2.json
Gives 0x3333…3333 a Permit2 allowance on 1 token(s). Permit2 can then move them for that spender without another signature.
Needs your attention:
• Unlimited Permit2 allowance for token 0x1111111111111111111111111111111111111111.
• Allowance never expires.
```
Part of a small family of inspection tools: [Calldata Lens](https://github.com/shelbygrayman/calldata-lens) · [Typed Intent](https://github.com/shelbygrayman/typed-intent) · [Approval Lens](https://github.com/shelbygrayman/approval-lens) · [EVM Wallet Lens](https://github.com/shelbygrayman/evm-wallet-lens)
## Why
Agents are starting to sit between people and their wallets. An agent that can say "this signature gives an unknown address an unlimited, never-expiring Permit2 allowance" before the user clicks is more useful than one that only says "transaction ready". This server gives any MCP client that ability with three read-only tools.
## What it reads
| Input | Example | What you get |
| --- | --- | --- |
| Raw calldata | `0x095ea7b3…` | approve, transfer, transferFrom, setApprovalForAll, safeTransferFrom (strict decoding) |
| Transaction object | `{ "to", "data", "value" }` | the above plus native value, contract deployment and address checks |
| JSON-RPC request | `eth_sendTransaction`, `eth_signTypedData_v4`, `personal_sign`, `eth_sign` | routed to the right reader, signer recorded |
| EIP-712 typed data | ERC-2612 `Permit`, Permit2 `PermitSingle` / `PermitBatch` / `PermitTransferFrom` | domain hash, message hash, digest, permit interpretation |
| personal_sign | hex or text | readable text, Sign-In with Ethereum detection, opaque bytes warning |
### Flags it raises
- Unlimited or extremely large ERC-20 allowances
- Collection-wide NFT operator permissions
- Unlimited or never-expiring Permit2 allowances
- A "Permit2" domain that points at a contract other than the canonical Permit2 address
- Typed data with no `chainId`
- A permit `owner` that is not the account being asked to sign
- Native value attached to an approval
- Opaque `personal_sign` bytes and any `eth_sign` request
## Use it with an MCP client
From a clone (works today):
```bash
git clone https://github.com/shelbygrayman/read-before-sign
cd read-before-sign && npm ci
claude mcp add read-before-sign -- node "$PWD/bin/read-before-sign.mjs"
```
Once published to npm, the clone step becomes `claude mcp add read-before-sign -- npx -y read-before-sign`.
For other clients, point the server command at `node /absolute/path/to/bin/read-before-sign.mjs`. Ready-made snippets for Claude Desktop, Cursor and Codex are in [`examples/`](examples).
Then ask your agent something like: *"Before I sign this, explain it: `{ paste the request }`"*.
### Tools
| Tool | Purpose |
| --- | --- |
| `explain_signing_request` | One entry point for any request shape. Returns a briefing with `summary`, `attention`, `notVerified`, `context` and the full decoder output. |
| `decode_calldata` | Strict static decoding of six common token calls. |
| `inspect_typed_data` | EIP-712 validation and digests, with an ERC-2612 interpretation. |
All three are marked read-only and closed-world. Results come back as text and as `structuredContent` (`read-before-sign.briefing.v1`).
## CLI
```bash
node bin/read-before-sign.mjs explain 0x095ea7b3… # calldata
node bin/read-before-sign.mjs explain request.json --json # any JSON request, full output
cat request.json | node bin/read-before-sign.mjs explain - # stdin
```
`--standard erc20|erc721` and `--decimals <n>` change how amounts are described. Use them only when you know the contract.
## Validation
```bash
npm ci && npm test
```
22 tests: calldata encoded independently with ethers, EIP-712 digests checked against ethers, Permit2 edge cases, malformed and truncated input, a raw stdio session (handshake, notifications, unknown tools and methods, parse errors) and an end-to-end session with the official MCP TypeScript SDK client. CI runs on every push.
## Important boundaries
This is static reading, **not a simulator, an approval scanner, a contract audit or a safety verdict**.
- Selector matches are only matches. Selector collisions, proxies and custom implementations exist.
- Nothing about the chain is read: not the destination's code, not balances, not existing allowances, not whether execution succeeds.
- Typed-data domains are claims made by whoever built the request.
- Permit2 recognition is by domain name and message shape. A clean briefing does not mean a request is safe.
- Unknown selectors and unknown message types stay unknown on purpose.
The decoders are vendored from [Calldata Lens](https://github.com/shelbygrayman/calldata-lens) and [Typed Intent](https://github.com/shelbygrayman/typed-intent); see [VENDORED.md](VENDORED.md). Typed-data hashing uses [ethers](https://github.com/ethers-io/ethers.js), pinned.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues