Farol
Understands and reads Circle's FiatTokenProxy layout, including the pre-EIP-1967 ZeppelinOS implementation and admin slots, so that proxy upgrade authority and implementation codehash can be verified and displayed before a user signs.
Runs its contract cataloguing and transaction-planning flow against Ethereum mainnet (chainId 1, via FAROL_RPC_1), reading live extcodehash/tier data, simulating calls with eth_simulateV1, and producing reviewable plans for catalogued contracts on that chain.
Catalogues Tether USD deployments (e.g. the OP Stack bridged USDT on HashKey Chain), pinning their codehashes and exit paths, and distinguishes them from similarly named CCIP-bridged tokens such as OpenUSDT so an agent does not confuse one stablecoin contract with another.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Farolbuild a transaction plan to revoke my USDC approval on HyperIndex V3 SwapRouter"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Farol
The registry warns, the wallet signs.
Farol is Portuguese for lighthouse: it warns of the rocks, it does not promise the ship a clear crossing.
Farol is an MCP server that gives an AI agent a curator-signed catalog of smart contracts and turns an intent into a transaction plan a human reviews on a local page before signing. The MCP process never holds a private key. No MCP tool signs a transaction or broadcasts one; signing happens only on the review page, driven by the wallet already injected in the user's browser.
Built for the EAG Global Buildathon Floripa, on HashKey Chain.
Submission
EAG Global Buildathon Floripa, 12 September 2026 · https://devfolio.co/projects/farol-1d90
Track | Why |
Brazil Hackathon | Built in person at InPETU Hub, Sapiens Parque, Florianópolis. |
EAG track 4 — Application Middleware & Open-Source Tooling | Farol is not an application, it is the layer under one: an MCP server any agent host can mount, a documented signed-catalog format, and a review page any wallet or agent framework can adopt without adopting Farol. |
HSK Chain — Blockchain Infrastructure (also fits AI Agents) | See Technical integration: HashKey Chain below. No contract of ours is deployed; the product runs against contracts already on HSK. |
Where the rest lives: architecture in Architecture and How it works; features in Key features and The tools; install and run in Install and Run the demo; what is proven and what is not in What is verified and Limits; iteration plans in Roadmap.
Related MCP server: crypto-knowledge
The problem
An agent that can transact has to answer a question no chain answers for it: is this address the contract it claims to be? Today the agent guesses from a name, and the human is asked to approve calldata they cannot read.
Name collisions are not hypothetical. On HashKey Chain two verified contracts share the name
"HyperIndex V3 SwapRouter". A contract whose verified source calls itself "Mock USDC" reports its
symbol() as exactly USDC.
How it works
The agent calls
plan_buildwith a catalogued runbook, explicit calls, or a revoke request.The server resolves the plan into
{to, data, value}calls, computes a tier per call by readingextcodehashlive, and stores the result as an immutable plan on disk, keyed by the hash of its canonical JSON.A review URL on
127.0.0.1opens. The page fetches the plan from the server by id, never from the agent or the URL.The page runs
eth_simulateV1when it opens and re-checks flags at sign time, so a flag raised after the plan was built still blocks the sign button for the affected call.The human reads the tier, the simulation and the trust line, then signs with their own wallet. The page posts the
txHashback to the server; the agent learns it by pollingplan_status.
Key features
Four tiers, one refusal. Every call carries a tier computed against the chain, not against a cached list:
Tier | Meaning |
T0 | catalogued, codehash matches what a curator signed |
T1 | verified source, no curated entry |
T2 | unverified source |
T3 | catalogued address, deployed code does not match — hard refusal |
T3 is the only refusal in the system. Everything else proceeds with a badge, because
eth_simulateV1 runs and a decode is attempted on any bytecode, catalogued or not.
Proxies. An entry pins two codehashes: the proxy's and its implementation's. At plan time the client reads the EIP-1967 implementation slot and compares; if the implementation changed since the entry was reviewed, the call demotes from T0 to T1 rather than hard-failing — an upgrade is not by itself an attack. The admin slot is read too, so the page shows who can replace the contract underneath the user. For UUPS patterns where the slot is empty it says "not determinable from storage" rather than guessing. Circle's FiatTokenProxy keeps those values in the pre-EIP-1967 ZeppelinOS slots; Farol reads those as well.
Flags name selectors, not contracts, so an exit path such as a revoke stays callable on a
flagged contract. A flag blocks signing only when its signer is on the curator trust list and its
severity is not info; a flag from anyone else is still shown, never hidden. If the flag feed
cannot be read at all, plan_build refuses outright rather than proceeding as if there were no
flags — negative information fails closed.
Discovery filters, it never ranks. catalog_search returns results alphabetically and always
states how many catalogued entries matched out of how many exist on that chain, plus a note that
uncatalogued contracts may exist. Token symbols resolve only against the curator-signed token list;
an ambiguous symbol is refused rather than guessed.
The catalog carries the trust model, never a price. Who can upgrade the code, how funds exit, who can pause it — and the pause switch is read live, because a state cannot be signed in advance. No yield figure, no recommendation: a registry that carries those becomes a thing to spoof.
Audits are bound to deployed bytecode. An audit report scopes to a git commit; what a human
signs is bytecode at an address. Farol keys on (chainId, address, codehash) and says on every
read whether an attestation still describes the code that is deployed now.
The shipped catalog carries zero audit attestations, because nobody has audited these
contracts, and every entry says so.
The tools
Five MCP tools. Nothing here signs, broadcasts, or marks a plan as signed.
Tool | In | Out |
| chain, optional text/address/intent/asset filter | matching entries, alphabetical, ambiguity stated, coverage count |
| chain, address | entry, attestations, audit attestations with the codehash each auditor signed over and whether it still matches, active flags, upgrade authority, current tier, and the exit path with the pause switch read live |
| chain, |
|
|
|
|
|
|
|
There is no send, sign, or mark-signed tool, by design. Only a local HTTP endpoint the review
page calls (POST /t/<token>/plan/<id>/result) can record a txHash, and that endpoint is not
reachable from the MCP surface an agent uses.
Install
Prereqs: Node 20+.
git clone https://github.com/ronoel/farol-mcp.git
cd farol-mcp
npm installClaude Code
The repo ships .mcp.json, picked up automatically from the project root. Add your own signing
address, which is the operator's setting and never comes from the agent:
{
"mcpServers": {
"farol": {
"command": "npx",
"args": ["tsx", "src/index.ts"],
"env": { "FAROL_POLICY": "graduated", "FAROL_FROM": "0xYourAddress" }
}
}
}For Claude Desktop, use an absolute path to src/index.ts, since Desktop does not run from the
repo's working directory.
Environment variables
Variable | Purpose |
| The signing address. Set by the operator; if it is absent, |
|
|
| Curator private key. Read only by the signing scripts, never by the running server — the server process never holds a key. |
| Fixed port for the review server. Default |
|
|
| RPC overrides for HashKey Chain, HashKey Chain Testnet, Ethereum, Base and Base Sepolia. |
| Override for the curator trust list. Default: |
Run the demo
No MCP host needed. This starts the review server, builds a wrap plan of 0.01 HSK on HashKey Chain Testnet (133) and prints a review URL — open it in the browser that holds your wallet:
npm run demo:plan -- <your-address> 133Use 177 for HashKey Chain mainnet. Testnet funds: https://hsk.xyz/faucet
Rehearse the one refusal. T3 is a catalogued address whose live code no longer matches the
signed codehash, and a live chain will not produce one on demand. This copies the catalog to a temp
directory, rewrites one codehash to a value the chain does not have, re-signs it with the real
curator key, and prints a FAROL_CATALOG to run against. plan_build then refuses for real,
naming both hashes. The repo's catalog is never touched.
npm run demo:t3 -- 8453 0x4200000000000000000000000000000000000006Rehearse an audit bound to a deployed codehash:
npm run demo:audit -- 8453 # the attestation matches the deployed code
npm run demo:audit -- 8453 0x4200000000000000000000000000000000000006 --staleTechnical integration: HashKey Chain
Farol deploys no contract of its own. It is middleware over contracts that are already on HSK Chain, and everything it says about them is re-read from the chain at plan time.
5 curator-signed entries on HSK mainnet (177), each pinning a codehash read from mainnet:
Wrapped HSK
0xB210D2120d57b758EE163cFfb43e73728c471Cf1— wrap / unwrap.Tether USD, OP Stack bridged
0xF1B50eD67A9e2CC94Ad3c477779E2d4cBfFf9029— the canonical bridged USDT; withdrawals to L1 burn it, so no approve is needed on that path.OpenUSDT, CCIP-bridged
0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189— what a CCIP bridge UI delivers when it says "USDT". Not the Tether USDT the HSK pools use.HyperIndex V3 SwapRouter
0xC161Db322043e94D0395673ab5A74F89088EC3b8— pins the codehash of one of the two verified contracts sharing this name on HSK. The other has no entry and resolves at T1.Chainlink CCIP Router
0xf2Fd62c083F3BF324e99ce157D1a42d7EbA77f1d— read-only entry, trust model recorded as facts, no execution runbook.
1 entry on HSK testnet (133): the OP Stack WETH predeploy
0x4200000000000000000000000000000000000006, whose on-chainname()reads "Wrapped HashKey EcoPoints" — the wrap/unwrap rehearsal.Curator-signed token lists for 177 and 133, so an agent resolving "USDT" on HSK resolves it against signed data instead of guessing.
Tiering is live against HSK, not precomputed: every
plan_buildre-readsextcodehashon 177 or 133 and compares it to the signed entry.Transactions are built and human-signed on HSK: the wrap runbook on 133 produces a real signed transaction through the review page; the swap runbook on 177 produces the two-step approve +
exactInputSingleplan, with the approve scoped to the exact amount, never unlimited.4 of the 12 live network tests run against HSK (chains 177 and 133).
Entries also ship for Ethereum (1), Base (8453) and Base Sepolia (84532), which is what shows the runbook templates are chain-agnostic rather than hardcoded to one deployment.
Catalog format
An entry is produced, then signed, then loaded only if the signature checks out:
npm run keygen >> .env
# add the printed address to catalog/curators.json (or set FAROL_CURATORS)
npm run entry:make -- <chainId> <address> --kind token --name X
# edit runbooks, intents, attestations and notes by hand in the written JSON
npm run entry:sign -- catalog/entries/<chain>/<file>.jsonThe signature is an EIP-191 personal_sign over the canonical JSON of the raw body: keys
sorted recursively, undefined dropped, no schema defaults applied — so what gets signed is
exactly the bytes on disk. entry:sign --role author produces a protocol team's own signature over
the same entry.
Architecture
src/mcp.ts the five tools; the whole surface an agent can reach
src/plan.ts intent or runbook -> {to, data, value} calls, tier per call
src/tier.ts T0/T1/T2/T3 against a live extcodehash, proxy slots included
src/catalog.ts load + verify signatures against the curator trust list
src/canonical.ts canonical JSON: what a signature actually covers
src/chain.ts RPC: extcodehash, storage slots, eth_simulateV1
src/http.ts the local review server; the only path that can record a txHash
src/page/ the review page: plain HTML and JS, no framework
src/store.ts immutable plans on disk, keyed by their own hash
catalog/ signed entries, token lists, flags, curator trust listWhat is verified
npm test — 8 suites, 108 tests: canonical JSON and signatures, the tier system, plan-building
rules including flags and revoke, the review server's HTTP rules, the MCP tool surface, audit
attestations and their codehash binding, the review page driven in jsdom against a fake wallet
provider, and repo hygiene (no console.log in src/, since stdout is the MCP transport; every
shipped catalog document verifying against the shipped curator list).
npm run test:live — 12 network tests against real chains: 4 on HashKey Chain mainnet, HashKey
Chain testnet and Ethereum; 8 on Base Sepolia.
Not verified, and worth saying so:
Signing with a real MetaMask on 133 or 177 is not covered by an automated test; the page's wallet interaction is tested only against a fake injected provider in jsdom.
EIP-7702 batching (
wallet_getCapabilities) is not implemented.
Limits
eth_simulateV1 returns logs, return data and native transfers; it does not return a storage
diff. A call can move state the simulation does not surface as a log.
A compromised curator key can sign a forged entry, and the codehash check will not catch it — the attacker computes the codehash correctly. The floor under that failure is the review-screen invariant: what the human reads on the page derives from calldata, the ABI and the simulation, never from runbook prose an agent or a curator wrote.
The catalog raises the ceiling; the simulation holds the floor. Neither claims more than what it checked.
Roadmap
A public, read-only Streamable HTTP surface (
catalog_search,catalog_get,tx_explain) with no wallet path, so the catalog is usable without running a server.The signed flag feed as a published format with a small reference client, and its timestamp anchored on Ethereum L1 via EAS.
Real audit attestations from auditors willing to sign over a deployment rather than a commit — the format is implemented and shipping empty on purpose.
EIP-7702 batching, so a multi-step runbook is one signature instead of several.
License
GNU Affero General Public License v3.0 only (AGPL-3.0-only). See LICENSE.
Open source, and deliberately the strongest copyleft: anyone who modifies Farol and runs it as a network service has to publish their changes under the same licence. The review page is a trust boundary — a closed fork of it would be a fork nobody can audit, which is the one thing this project cannot allow. Commercial licences under other terms are available on request.
This server cannot be deployed
Maintenance
Related MCP Connectors
Pre-execution governance for AI agents. Deterministic PASS/FAIL/REVIEW verdicts, replayable proof.
Read-only smart-contract security intelligence for autonomous agents.
Supervised API-write gateway for AI agents with policy, human approval and execution receipts.
Wallet-signed Solana RPC for AI agents. No API keys, LLM-safe amounts, pay-per-call in SOL.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to resolve smart contract ABIs, read, encode, simulate, and prepare transactions across multiple blockchains via a REST API or MCP server, with no signing required.-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to perform complex crypto operations like cross-chain routing, contract decoding, portfolio management, and anti-rug security checks, returning unsigned transactions for safe signing by the agent.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to analyze Ethereum wallets, simulate transactions, and draft transfers with deterministic policy and risk scoring, requiring human approval before on-chain execution.7 npmISC
- AlicenseAqualityAmaintenanceMCP server that enables AI signing agents to pre-flight transactions by checking ERC-7730 descriptor safety, explaining function semantics, and scanning contracts for danger primitives.4MIT