Pre-sign transaction-safety checks for signing agents (ERC-7730 descriptors).
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
Tooling to author, verify, and attest ERC-7730 Clear Signing descriptors for the public registry that compatible wallets read from.
A descriptor is a JSON file that tells a wallet how to render a contract call in plain language, so users see what they are signing instead of raw hex. Lucent covers the full path: find contracts that lack a descriptor, write and harden one, check it beyond schema validity, prove it against real transactions, and produce an ERC-8176 attestation.
Lucent's primary product surface is a call-scoped HTTP API with a stateless analysis core and bounded access/payment ledgers. It binds one unsigned EVM call (including sender) to one descriptor deployment, resolves the exact calldata selector, decodes the arguments, and runs the audit, comprehension, and danger checks only for that selected function:
POST /v1/preflight returns call and assessment fingerprints with a
safe_to_present, review, or block decision. A blocked call is a successful
assessment and therefore returns HTTP 200; malformed, unbound, ambiguous, or
undecodable input returns a stable application/problem+json error.
The words are intentional: safe_to_present never means safe to execute.
Version 1 uses a deliberately narrow scalar presentation profile. Local mode
can analyze caller-supplied ABI data; protected deployments can instead require
a finalized runtime-bytecode match and Sourcify ABI before a verdict is
returned. Hosted V1 rejects proxy-backed deployments until dispatch and
upgrade-state semantics can be proven, including custom delegatecall
dispatchers without EIP-1967 slots. Neither mode simulates state, detects
economic exploits, or judges the counterparty. Those limits ride in every
response. Full contract and deployment modes:
docs/HOSTED-API.md.
The API and container start disabled. make api opts into loopback-only local
development (open, verified source off, x402 off). Production modes are
configured explicitly at startup and fail closed when their dependencies are
incoherent:
api_key β hashed tenant keys, per-tenant token buckets, and five-minute
idempotent result replay;x402 β official x402 v2 challenge/signature/receipt headers and exact USDC
settlement on Base mainnet;api_key_or_x402 β subscription/quota access for integrations, with USDC
pay-per-request fallback.Any mode that accepts x402 requires verified-source mode. Payment proofs are verified before Lucent uses RPC/Sourcify capacity, settled only after a complete result exists. Exact retries replay the retained receipt, while a separate authorization ledger prevents the same signed EIP-3009 transfer from being rewrapped under a new idempotency key. Paid outcomes are not evicted while unexpired, and authorizations must expire within the server-owned five-minute window. The container runs one worker by default; scale-out requires a shared quota/idempotency/authorization-claim backend.
An AI agent about to sign a transaction faces exactly the question Lucent's
checks answer β is this call clear enough to present, and does the screen a
human would see actually describe what it does? scripts/mcp_server.py exposes
that as an MCP server (JSON-RPC over stdio) so an agent can pre-flight a signature:
preflight_transaction β the primary transaction-time gate. It binds
chain_id, from, to, data, and value to a matching descriptor
deployment, decodes one unique selector, analyzes only that function, and
fingerprints the complete request.check_descriptor β an authoring-time report over an ERC-7730 descriptor:
the audit grade (screen shows the right fields), the comprehension grade (a
plain-language consequence sentence + risk tier per function), and the danger
scan (structural primitives a clear screen can't make safe). It is not bound
to a pending call and must never authorize one.explain_signature β an unbound actorβactionβobject sentence + risk tier
for descriptor-authoring and UX-copy review, never transaction approval.scan_contract β danger-scan a deployed contract by address (fetches the
verified ABI from Sourcify), so an agent can assess a contract before any
transaction is built.Register it as a stdio MCP server pointing at scripts/mcp_server.py from the
repo root (see mcp.json). Same transport shape as the sibling Groundcheck and
Seiche servers.
For every pending call, use preflight_transaction. Its model-facing output
hashes arbitrary string calldata, and its server-owned consequence sentence
never incorporates descriptor-authored prose.
Most stages that read on-chain data need a free Etherscan API key:
| Stage | Script | Purpose |
|---|---|---|
| Discover | discover.py | Classify candidates: verified, signable, and uncovered |
| Fetch ABI | fetch_abi.py | Verified ABI from Sourcify (a registry requirement) |
| Resolve proxy | resolve_proxy.py | Cache an implementation ABI under a proxy address |
| Generate | erc7730 generate | Bootstrap a draft descriptor |
| Lint | erc7730 lint | Schema, selectors, device limits, ABI consistency |
| Audit | audit.py | Grade the descriptor on screen trustworthiness |
| Comprehend | comprehend.py | Grade the descriptor on human comprehension risk |
| Danger | danger.py | Flag structural danger primitives a clear screen can't make safe |
| Verify | semverify.py | Check the screen against real on-chain movements |
| Prove | preview.py, fetch_tx.py | Render the screen and build real test vectors |
| Package | to_submission.py | Registry-form output under dist/, gated on audit grade |
| Attest | attest.py | ERC-8176 attestation over the descriptor hash |
| Watch | watch.py | Monitor merged descriptors for drift |
| Review | review.py | All checks composed into one publishable report |
A common.py module holds the shared Sourcify and Etherscan clients and ABI
utilities. lucent/preflight.py is the shared call-scoped decision core used by
both HTTP and MCP, so transports cannot drift on verdict policy.
erc7730 lint checks that a descriptor is well-formed. audit.py checks whether
the on-device screen would mislead a user, which lint does not:
@.value, or a tokenAmount
with no known token.It reports a letter grade. to_submission.py refuses to package below grade B.
A raw generated draft of the ENS controller scores F; the hardened descriptors
score A.
Lint checks that a descriptor is well-formed; audit.py checks that the screen
shows the right fields. Neither asks the question that
"What I Sign Is Not What I See" shows is the
real failure: users mis-understand a technically-correct screen. Its studies
found people fixate on the amount and recipient and miss scope, delegation, and
unlimited allowances β and that a bare field list, even a complete one, leaves
comprehension at chance on the dangerous cases. Its Signature Semantic Decoder
cut false approvals on unlimited-allowance and phishing transactions by 73% and
46% by rendering an actorβactionβobject sentence and a risk tier with a reason.
comprehend.py brings that to the descriptor. For each signable function it
emits:
setApprovalForAll), ERC-20 allowances (flagged
distinctly from ERC-721 token-id approvals, since approve(address,uint256)
reads identically but means different things), permits (off-chain, invisible in
history), admin/upgrade authority, and raw-hex recipients (the address-poisoning
surface).An unrecognised function with no on-screen intent is reported as an unexplained
screen (a caution), never silently cleared β an unexplained screen invites blind
approval, which is the failure the paper measures. Run it with make comprehend DESC=β¦; NameWrapper's setApprovalForAll and the controller's
transferOwnership both surface as CRITICAL comprehension risks that lint and
audit pass.
Audit asks whether the screen shows the right fields; comprehend asks whether the
human understands them. danger.py asks the third question: can this function,
by construction, do something a clear screen still can't make safe? A descriptor
can render a perfectly honest sentence for execute(address target, bytes data) β
"Call {target} with {data}" β and that call can still drain the wallet, because
the primitive itself is unbounded.
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/lucent)<a href="https://allmcps.com/mcp/lucent"><img src="https://allmcps.com/api/badge/lucent?style=directory" alt="Lucent on AllMCPs" /></a>