presign-guard

MCP serverAI & models

Pre-sign, token and approval checks for agents: green/orange/red for signatures, tokens, wallets.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the presign quick check tool from presign-guard

Install presign-guard

The server’s own address, for the clients that take one directly. Or connect ahel onceand every client you use reads it from one address, with the account kept on ahel rather than in each client’s config.

  • Claude Code

    claude mcp add --transport http --scope user presign-guard 'https://presign-guard.fizzl.eu/mcp'

    Run it once in your project, then open /mcp to approve any sign-in the server asks for.

  • Claude Desktop

    https://presign-guard.fizzl.eu/mcp

    Add a custom connector in Settings, paste this address, and approve the sign-in.

  • Cursor

    cursor://anysphere.cursor-deeplink/mcp/install?name=presign-guard&config=eyJ1cmwiOiJodHRwczovL3ByZXNpZ24tZ3VhcmQuZml6emwuZXUvbWNwIn0=

    Open the link and Cursor adds the server at that address.

  • ChatGPT

    https://presign-guard.fizzl.eu/mcp

    In Settings, enable Developer mode, create an MCP app, and paste this address. Your plan and workspace must allow custom apps.

  • Codex

    codex mcp add presign-guard --url 'https://presign-guard.fizzl.eu/mcp'

    Run it once, then sign in with codex mcp login presign-guard if the server asks for an account.

From the project's README

As published by fizzl13/presign-guard in README.md.

A pre-sign risk check for AI agents. Before an agent signs a transaction, approval, or EIP-712 signature, it pays a few cents per call over x402 and gets back a green / orange / red verdict with machine-readable reason codes. Optionally, it also gets a plain-language explanation in Dutch or English.

Watch the 1-minute explainer: presign-guard.onrender.com/media/explainer.mp4

New: the token verdict in 45 seconds: presign-guard.onrender.com/media/token.mp4

Part of Klaartaal by FIZZL AI.

Endpoints

RoutePriceReturns
POST /v1/check$0.01 USDCVerdict, reason codes, decoded subject
POST /v1/check/explain$0.03 USDCThe same, plus a plain-language explanation (lang: "nl" or "en")
GET /v1/token?chain=…&address=…$0.01 USDC, Base or SolanaToken verdict: grade, reason codes, one-line summary, market data (see below)
GET /v1/approvals?chain=…&address=…$0.02 USDC, Base or SolanaWallet approval audit: every open token approval, its spender, and which to revoke (see below)
POST /mcpfree / paidMCP server (Streamable HTTP): see below
GET /healthfreeLiveness
GET /openapi.jsonfreeOpenAPI 3.1 spec with prices (x-payment-info)
GET /.well-known/x402freex402 discovery manifest

Payment is x402 v2 with the exact scheme, in USDC on Base (the token verdict and the approval audit also on Solana). The 402 carries Bazaar discovery metadata (input example, input and output schema), and the challenge is mirrored into the JSON body for clients that don't read the PAYMENT-REQUIRED header. You are never charged for an error. Invalid requests (400) and upstream outages (503) cancel settlement, and they always return verdict: null, never a guessed verdict.

MCP

https://presign-guard.onrender.com/mcp is an MCP server (Streamable HTTP, stateless) for Claude, Cursor and agent frameworks, listed in the official MCP registry as io.github.Fizzl13/presign-guard.

ToolPriceReturns
presign_quick_checkfree, 10 calls/hourThe verdict only (green, orange or red)
presign_check$0.01 USDC via x402The full verdict and reason codes, as POST /v1/check
presign_check_explain$0.03 USDC via x402The same plus a plain-language explanation, as POST /v1/check/explain
token_quick_verdictfree, shares the 10 calls/hourThe token verdict and grade only
token_verdict$0.01 USDC via x402The full token verdict, as GET /v1/token
wallet_approvals$0.02 USDC via x402The wallet approval audit, as GET /v1/approvals

The paid tools are paid inside the MCP call with the x402 MCP transport (_meta["x402/payment"]), on Base (token_verdict and wallet_approvals also on Solana), to the same payout wallets as the HTTP routes. Invalid input is refused before payment, and a failed check is not charged.

Token verdict

GET /v1/token?chain=solana&address=<mint> (or chain=base|ethereum|arbitrum|optimism|polygon|bsc with a 0x token contract) answers one question before an agent buys, holds or accepts a token: is the token itself a trap?

{
  "verdict": "orange",
  "grade": "RISKY",
  "one_liner": "RISKY: 1% transfer fee; $21k liquidity; 1 h old (+2 more)",
  "reasons": [{ "code": "TRANSFER_FEE", "severity": "orange", "details": { "feePct": 1 } }, "…"],
  "token": { "chain": "solana", "address": "…", "name": "…", "symbol": "…" },
  "market": { "priceUsd": 0.0004, "liquidityUsd": 21000, "marketCapUsd": 400000, "volume24hUsd": 90000, "firstPairAt": "…", "ageSeconds": 3600, "url": "https://dexscreener.com/…" },
  "sources": ["goplus", "dexscreener", "rugcheck"],
  "checkedAt": "…"
}

Grades: SAFE (green), CAUTION (one orange reason), RISKY (two or more), AVOID (red). The one-liner states facts only.

SeverityCodes
redRUGGED, NON_TRANSFERABLE, MALICIOUS_AUTHORITY; EVM: TOKEN_HONEYPOT, TOKEN_AIRDROP_SCAM, TOKEN_IMPERSONATION
orangeMINT_AUTHORITY_ACTIVE, FREEZE_AUTHORITY_ACTIVE, BALANCE_MUTABLE, CLOSABLE, TRANSFER_HOOK, TRANSFER_FEE, HIGH_TRANSFER_FEE (≥10%), TRANSFER_FEE_UPGRADABLE, LP_NOT_LOCKED (<50% locked, token younger than 30 days), LOW_LIQUIDITY (<$50k), NO_DEX_MARKET, NEW_TOKEN (<24 h), TOP_HOLDERS_CONCENTRATED (top holder >20% or top 10 >50%, pools and locked accounts excluded; on EVM only wallets count, not contracts); EVM: the GoPlus token codes of /v1/check (TOKEN_HIGH_TAX, TOKEN_UNVERIFIED, …) and TOKEN_CANNOT_BUY
infoMUTABLE_METADATA, NO_SOCIALS, TOKEN_ON_TRUST_LIST, NO_SECURITY_DATA, RUGCHECK_DANGER, RUGCHECK_UNAVAILABLE, LP_NOT_LOCKED on older tokens, on trust-list tokens (USDC, USDT, WETH): the issuer's powers, LP_NOT_LOCKED, LOW_LIQUIDITY and NO_DEX_MARKET (DexScreener undercounts quote assets)

On a token on the GoPlus trust list (USDC, USDT), the issuer's powers (mint, freeze, change balances) are info: the issuer keeps them on purpose. Missing data never makes a token red. If GoPlus or DexScreener is down there is no verdict (503, not charged); if RugCheck is down the verdict comes without it and says so.

Wallet approval audit

GET /v1/approvals?chain=base&address=<wallet> (or chain=ethereum|arbitrum|optimism|polygon|bsc) is the follow-up to /v1/check: that one asks "should I sign this approval?", this one asks "which approvals did I already give, and which should I revoke?". Agents with their own wallet can run it as a periodic check.

{
  "verdict": "red",
  "grade": "AVOID",
  "one_liner": "AVOID: 5 approvals, 1 to a flagged address, 1 to a plain wallet, 1 unlimited; revoke 3",
  "reasons": [{ "code": "SPENDER_MALICIOUS", "severity": "red", "details": { "count": 1, "spenders": ["0x…"] } }, "…"],
  "summary": { "approvals": 5, "tokens": 2, "unlimited": 2, "toRevoke": 3 },
  "approvals": [{
    "token": { "address": "0x…", "symbol": "USDC" },
    "spender": { "address": "0x…", "name": null, "trusted": false, "contract": true },
    "amount": "500", "unlimited": false, "approvedAt": "…", "severity": "red",
    "codes": [{ "code": "SPENDER_MALICIOUS", "severity": "red", "details": { "behaviors": ["phishing_activities"] } }],
    "revoke": true
  }, "…"],
  "revokeUrl": "https://revoke.cash/address/0x…?chainId=8453"
}
SeverityCodes (per approval; the wallet-level reasons count them)
redSPENDER_MALICIOUS (the spender is flagged by GoPlus)
orangeAPPROVAL_TO_WALLET (the spender is a plain wallet, not a contract), SPENDER_SUSPICIOUS (GoPlus doubt list), SPENDER_UNVERIFIED (contract source not verified), UNLIMITED_APPROVAL (to a spender not on the GoPlus trust list)
infoUNLIMITED_APPROVAL_TRUSTED (e.g. Permit2), STALE_APPROVAL (older than a year), TOKEN_FLAGGED (the approved token itself), NO_APPROVALS

Every approval with an orange or red code has revoke: true. The grades are the same as the token verdict. Source: GoPlus token_approval_security (ERC-20 allowances); NFT approvals are not covered. If GoPlus is down there is no verdict (503, not charged).

Request types

// Token approval
{ "type": "approval", "chainId": 8453, "token": "0x…", "spender": "0x…", "amount": "1000000" }

// Raw transaction (approve / increaseAllowance / setApprovalForAll are decoded)
{ "type": "transaction", "chainId": 8453, "to": "0x…", "data": "0x…", "value": "0" }

// EIP-712 signature: pass the eth_signTypedData_v4 payload as an object or JSON string
{ "type": "signature", "chainId": 8453, "typedData": { "domain": {…}, "types": {…}, "primaryType": "PermitSingle", "message": {…} } }

Supported chains: 1, 10, 56, 137, 8453, 42161.

Optional on every type: "origin": "https://…", the site asking for the signature or transaction (a URL or a hostname). Its domain age is looked up: a domain registered less than 30 days ago is orange (NEW_DOMAIN), which catches the fresh phishing sites wallet drainers run on. Local and IP origins are not looked up.

Recognised signatures: EIP-2612 Permit, DAI-style permit, Permit2 (PermitSingle, PermitBatch, PermitTransferFrom, batch and witness variants), EIP-3009 TransferWithAuthorization / ReceiveWithAuthorization (what x402 asks an agent to sign to pay), and Seaport OrderComponents.

An x402 payment moves one fixed amount to one recipient and grants no allowance, so paying a plain wallet is green (PAYMENT_AUTHORIZATION, info). It turns red if the recipient is flagged, and orange if the amount is effectively unlimited or the authorization stays valid for more than a month. Anything else comes back at least orange (UNRECOGNIZED_SIGNATURE).

Response

{
  "version": "2",
  "verdict": "red",
  "reasons": [
    { "code": "UNLIMITED_APPROVAL", "severity": "orange", "subject": "0x…", "details": { "token": "0x…" } },
    { "code": "SIGNATURE_GRANT_TO_EOA", "severity": "red", "subject": "0x…" }
  ],
  "subject": { "chainId": 8453, "kind": "permit2_allowance", "offchain": true, "grants": [ … ] },
  "scope": "…",
  "sources": ["goplus"],
  "checkedAt": "2026-09-23T12:00:00.000Z"
}

Every token that is approved, permitted or paid is also checked with GoPlus token security (honeypot, impersonation, owner powers, taxes). The verdict is the most severe reason: any red makes it red, otherwise any orange makes it orange. info reasons never change the verdict.

Reason codes

SeverityCodes
redPHISHING_ACTIVITIES, STEALING_ATTACK, SANCTIONED and other GoPlus address flags, SANCTIONED_ADDRESS (on the OFAC SDN list; details name the SDN entry and program), CREATOR_OF_MALICIOUS_CONTRACTS, MALICIOUS_CONTRACT_BEHAVIOR, ON_DOUBT_LIST, UNLIMITED_APPROVAL_TO_EOA, SIGNATURE_GRANT_TO_EOA, ORDER_PAYS_YOU_NOTHING, and for the token itself TOKEN_HONEYPOT, TOKEN_IMPERSONATION (details name the real token), TOKEN_AIRDROP_SCAM
orangeUNLIMITED_APPROVAL, UNLIMITED_TRANSFER, APPROVAL_FOR_ALL, APPROVAL_TO_EOA, SIGNATURE_TRANSFER, LONG_LIVED_PERMISSION, NONCANONICAL_PERMIT2, UNVERIFIED_CONTRACT, RECENTLY_DEPLOYED, MARKETPLACE_ORDER, UNRECOGNIZED_SIGNATURE, BLACKLIST_DOUBT, MIXER, NEW_DOMAIN (origin registered under 30 days ago), DOMAIN_NOT_REGISTERED, and for the token TOKEN_OWNER_CAN_CHANGE_BALANCES, TOKEN_OWNERSHIP_RECLAIMABLE, TOKEN_HIDDEN_OWNER, TOKEN_SELFDESTRUCT, TOKEN_CANNOT_SELL_ALL, TOKEN_CREATOR_MADE_HONEYPOTS, TOKEN_HIGH_TAX (buy or sell tax of 10% or more), TOKEN_UNVERIFIED
infoEIP7702_DELEGATED_WALLET (a plain wallet with EIP-7702 code, treated as a wallet), PARTIAL_SOURCE_DATA (GoPlus returned partial data for this address), PAYMENT_AUTHORIZATION, REVOKES_APPROVAL, OFFCHAIN_SIGNATURE, SIGNATURE_EXPIRED, UPGRADEABLE_PROXY, ON_TRUST_LIST, UNDECODED_CALL, and issuer controls on the token (USDC has several): TOKEN_MINTABLE, TOKEN_PAUSABLE, TOKEN_BLACKLIST, TOKEN_UPGRADEABLE, TOKEN_TAX_MODIFIABLE, TOKEN_TRADING_COOLDOWN, TOKEN_TAX, TOKEN_ON_TRUST_LIST, TOKEN_NO_SECURITY_DATA (GoPlus has no record of the token), DOMAIN_AGE, DOMAIN_AGE_UNKNOWN (no RDAP data for that TLD), and when PG1 can't be reached SANCTIONS_SCREEN_UNAVAILABLE / DOMAIN_AGE_UNAVAILABLE (the check goes on; GoPlus still carries a sanctions flag)

Not covered

eth_sign and personal_sign messages, and transaction simulation. Treat a green verdict as "no known risk signals", not as a guarantee.

Run locally

cp .env.example .env   # fill in PAY_TO and ANTHROPIC_API_KEY
npm install
npm test               # 36 tests, network mocked
npm run dev

End-to-end payment test (Base Sepolia)

Fund a throwaway wallet with Base Sepolia test USDC, set AGENT_PRIVATE_KEY and CHECK_URL in .env, then run:

npm run client

The script makes a valid call, which should return 200 with a settlement receipt, and an invalid call, which should return 400 with no charge.

Deploy to Render

The live service runs on Base mainnet: render.yaml sets X402_NETWORK=eip155:8453, which needs CDP_API_KEY_ID and CDP_API_KEY_SECRET (Coinbase CDP facilitator; the service refuses to start on mainnet without them). After the first paid call settles through CDP, the routes are listed in the CDP Bazaar. To test without real money, set X402_NETWORK=eip155:84532 (Base Sepolia, public x402.org facilitator); without X402_NETWORK the code also defaults to Base Sepolia.

PAY_TO_SOLANA (optional) is a Solana address for USDC payments on Solana, offered for the token verdict only; those payments settle through the PayAI facilitator (SOLANA_FACILITATOR_URL to override). Without it, the token verdict is paid on Base only.

PAY_TO must be an EVM address (0x + 40 hex characters). Surrounding spaces are trimmed; anything else stops the server at startup with a clear error, so a typo can't publish an unpayable 402.

Data sources

Risk data comes from the GoPlus Security API. OFAC SDN sanctions screening and domain age come from PG1 (public OFAC and RDAP data, credited as pg1 in sources; set PG1_API_KEY to a PG1 membership key to be exempt from PG1's anonymous rate limit); the token verdict adds RugCheck (Solana) and DexScreener (market data). Plus eth_getCode on a public RPC to recognise EIP-7702 wallets (override with RPC_URL_<chainId>). Explanations come from Claude (Anthropic).

Tools it offers (7)

What this server listed when ahel dialed its public endpoint in Sep 2026, with no key and no account of yours. The names are the server’s own.

  • presign_quick_check
  • token_quick_verdict
  • presign_check
  • presign_check_explain
  • token_verdict
  • wallet_approvals
  • feedback

Signals

GitHub stars
1
Last commit
Sep 2026
Advanced
Delivery
presign-guard MCP server → your ahel connector (mcp.ahel.ai) → your AI.
Item type
mcp-server
Key
io-github-fizzl13-presign-guard
Source
github.com/fizzl13/presign-guard
Hosted endpoint
https://presign-guard.fizzl.eu/mcp