Check a target before you install, connect, pay or trade
onchain_agent_preflightUSE WHEN you are about to install a package, clone a repo, connect to an MCP endpoint, pay an agent, or trade a token, and you want to know what is on record about it FIRST — every evidence line, with the field it was read from and its date. Stack picks and deploy specs already carry a records-only preflight_summary; call this for the full evidence, for any target that is not a pick (a repo, package, token, skill or ERC-8004 agent you were handed), or for a live handshake with an unlisted MCP endpoint. Pass exactly one of repo, package, endpoint, agent, token, skill, x402 or address.
Returns a verdict — go | caution | no | unknown — with one evidence line per check, each naming the field it was read from and when that field was written. The rules are written down in lib/preflight.ts and cited by id in rule.
RULE ENFORCED: a verdict names WHAT WAS CHECKED AND WHEN. It is never a security review, a quality judgment or a statement about returns, and unknown means Sato Hub holds no record — not that something is wrong. An unlisted endpoint gets ONE live handshake (initialize + tools/list, 8 s cap) and can never come back go: a handshake is not a record. For agent=: we confirm the ERC-8004 registration exists, fetch its registration file, and report the services it DECLARES; only a declared MCP service is probed.
TOKEN LANE (token + chain; the EVM chains listed, and Solana): keyless chain reads — bytecode presence and size, the ERC-20 views, the Clanker v4 factory's OWN deployment record (tokenDeploymentInfo, not a bytecode heuristic), and the Uniswap v3 factory across the four standard fee tiers against wrapped native. Every field is nullable and a null carries the reason it is null. ON SOLANA (chain Solana, token = the mint): the mint's own account is read — token program (SPL or Token-2022), decimals, supply, mint and freeze authority (a revoked authority is a known fact, distinct from an unread one), the Token-2022 extensions (PermanentDelegate, TransferHook, transfer fee with its bps, NonTransferable), the ten largest token accounts as a share of supply (token accounts, not wallets: a pool or curve vault counts), and, for a mint with pump.fun's vanity suffix, the bonding-curve reserves and completion flag. A PermanentDelegate, a TransferHook or a transfer fee is a caution with the plain reason; a NonTransferable mint is a no; there is no go for Solana. Each line carries its date. PERMANENTLY NULL on EVM, and said so in the evidence: holder concentration (no keyless public source — explorers are not scraped); Uniswap v4 / non-Uniswap liquidity is checked once, with DexScreener, only when the v3 lookup and the launch-venue match are both empty (a v4 poolId cannot be reconstructed without the PoolKey). The deployer address needs an optional explorer key. A pool existing is not depth; a locker holds a position on the terms its own code enforces. Nothing in this lane says safe, audited, rug or scam — those are not readings.
SKILL LANE (skill): a skill is a DOCUMENT an agent follows, which is exactly why it is worth checking first — the ClawSwarm skills needed no malware, only text telling the agent to generate a wallet and post the private key. Evidence is the static disclosure the weekly sweep already produced: hosts the text names, whether it generates or handles keys, whether it asks for a credential, whether it pipes a remote script into a shell, what tools it grants itself — each finding WITH the lines that produced it — plus installs, when it was last seen in its registry, and whether a host it names belongs to a listed project. Nothing is fetched from a registry and no skill is executed. A DISCLOSURE DESCRIBES: it never says safe, it never says malicious, an empty flag list is "nothing matched" rather than a pass, and the registry's own scan result is attributed to that registry by name.
Returns (json): { verdict, rule, target: { kind, value, slug, name, sato_url, verify_url }, evidence: [{ check, result, source_field, checked_at }], checked_at, caveat, rules, token?, skill?, solana_receipt? }. token and skill are the raw reports for those lanes; solana_receipt (present with a custody reading) is where that reading is recorded on Solana, or null. Read-only.
X402 LANE (x402): one unpaid request to the resource URL; reports whether it answered 402 and the payment terms it states (network, asset, amount, payTo), against its catalogue entry and our daily record when we hold one. It describes the payee — it is never a reputation score. CUSTODY: repo, package, endpoint and skill results carry custody (Sato Check: does it take your key, does the key leave, can it move funds, what changed) when a profile is on record; custody can only lower a verdict, never raise it.
ADDRESS LANE (address + chain, optional origin and from): the Sato Scan recipient check before a payment. Five questions, each with its date and its own gap — does the address only look like one the payer pays, is the token the issuer's contract, did the paid party declare the address, does the address carry a structural mark, is the paying wallet targeted by address poisoning. Rules A1-A7. A reading describes; unknown is no record either way, and go is declared, canonical and nothing found within the stated limits, never a clearance. The address, origin and paying wallet are not stored. For the reading on its own, call onchain_agent_scan_recipient.
Example: { repo: "coinbase/agentkit" } · { endpoint: "https://mcp.example.com/v1" } · { x402: "https://api.example.com/paid" } · { agent: "base:42" } · { token: "0x1bc0c42215582d5A085795f4baDbaC3ff36d1Bcb", chain: "Base" } · { skill: "clawhub/solana-wallet" } · { address: "0x1bc0c42215582d5A085795f4baDbaC3ff36d1Bcb", chain: "Base" }
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| from | No | With `address`: the paying wallet. Lets the reading compare against its own history and say whether it is being targeted by address poisoning. Never stored. | |
| repo | No | A GitHub repository: a URL or bare owner/name, e.g. 'coinbase/agentkit'. | |
| x402 | No | An https x402 resource URL. ONE unpaid request reads its 402 payment terms (v1 body or v2 PAYMENT-REQUIRED header); no payment is ever sent or signed. | |
| agent | No | An ERC-8004 agent reference, <chain>:<id>, e.g. 'base:42'. Chains: Ethereum, Base, Arbitrum, Optimism, Polygon, BNB Chain, Avalanche, Gnosis, Robinhood Chain, Celo. | |
| chain | No | Chain for `token`: Base, Ethereum, Arbitrum, Robinhood Chain, Solana. Anything else answers 'unknown' with the reason, never a guess. For `address` it is the chain the payment would settle on: Base, Solana, Tempo, Polygon, BNB Chain, Arbitrum, Avalanche, Optimism, Ethereum, SKALE Base, Sei, X Layer, Monad, Robinhood Chain, World Chain, Abstract. | |
| skill | No | An agent skill: '<registry>/<id>' (registries: clawhub, skillssh, skills.sh, skills-sh, github), or the skill id alone when it is unique. Reads the static disclosure already on record — nothing is fetched from a registry and no skill is ever executed. | |
| token | No | An ERC-20 token contract address, e.g. '0x1bc0c42215582d5A085795f4baDbaC3ff36d1Bcb', or with chain Solana a mint (base58). Needs `chain`. | |
| origin | No | With `address`: the https URL whose 402 response named the address (a public host; no credentials). Lets the reading say whether that origin declared it. Never stored. | |
| address | No | A recipient address the caller is about to pay (Sato Scan): 0x + 40 hex on an EVM chain, or a Solana address. Needs `chain`. Never stored. | |
| package | No | An npm or PyPI package name, e.g. 'solana-agent-kit'. A trailing @version is ignored. | |
| endpoint | No | An https MCP endpoint URL. If it is not in the directory it gets a live initialize + tools/list handshake, capped at 8 seconds. | |
| x402_method | No | With x402: the method the paid request uses; the unpaid probe uses the same one (a body method sends an empty JSON body, never yours). Default: GET, then POST on a 405. | |
| response_format | No | Text format; structuredContent is JSON either way. | markdown |