VetAgent
VetAgent
A pre-trade safety check for AI agents. Before an agent buys, holds, or recommends
a token, it calls VetAgent and gets an actionable verdict — low / medium / high /
unknown — plus the specific signals behind it, instead of a wall of numbers to interpret.
Remote MCP endpoint: https://vetagent.dev/mcp · Landing page: https://vetagent.dev
Using it, or think it got a token wrong? hello@vetagent.dev — or open an issue.
// assess_token_risk("0x…", chain_hint="ethereum")
{
"risk_level": "medium",
"risk_score": 36,
"confidence": "high",
"signals": [
{"severity": "ok", "name": "Liquidity is adequate", "category": "liquidity"},
{"severity": "ok", "name": "Buys and sells normally", "category": "honeypot"},
{"severity": "warn", "name": "Contract is closed source", "category": "contract"}
],
"recommendation": "Medium risk. Real signals fired but none are fatal. Review liquidity, holder distribution and contract permissions before deciding."
}Install
Nothing to install. It is a remote server: no package, no container, no API key, no signup. Add one URL.
Claude Code:
claude mcp add --transport http vetagent https://vetagent.dev/mcpCline, Cursor, Claude Desktop, or any other MCP client — add to the MCP config:
{
"mcpServers": {
"vetagent": {
"type": "http",
"url": "https://vetagent.dev/mcp"
}
}
}Confirm it worked — three tools should come back:
curl -s -X POST https://vetagent.dev/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Not using MCP? The same engine answers over plain HTTP:
GET https://vetagent.dev/assess/<address>
See llms-install.md for the version written for an agent doing the
installing, and docs/AGENT-INTEGRATION.md for how to use
the output once connected.
Related MCP server: contract-auditor
unknown is not low
This is the single most important thing to know about the output.
VetAgent is fail-closed. When a check cannot run — an upstream is down, a
buy/sell simulation fails, no liquidity data comes back — it returns unknown and
lists exactly what was missing in evidence.data_gaps. It never substitutes an
optimistic middle value, and it never sizes a position for you.
confidence measures how complete the input data was, not how safe the token is.
A risk tool that honestly says "I don't know" is useful. One that guesses is not.
Tools
Tool | What it does |
| Full risk profile: sellability simulation, liquidity depth, pair age, holder concentration, contract permissions, upstream aggregate verdicts |
| Liquidity snapshot for the primary pair, with an explicit |
| Newest / hottest pools on a chain. Discovery only — not a safety endorsement |
Pass chain_hint whenever you know it. Ethereum forks such as PulseChain inherit
contract addresses, so the same address exists on multiple chains with wildly
different prices; the hint removes that ambiguity. (Without it, VetAgent prefers
canonical chains — see _CHAIN_RANK in src/risk.py.)
Full agent-facing contract: docs/AGENT-INTEGRATION.md.
Accuracy benchmark
Most token-risk tools publish a feature list. We publish our error rates and the method behind them — including the parts that do not work yet — against labels produced by data sources the engine does not read.
What is measured today: a 4.3% false positive rate on 162 healthy tokens, a 15.3% unknown rate, and 22.9% of GoPlus-tagged centralised tokens rated high -- almost all abandoned pools holding cents, not USDT, which is rated low.
What is not measured today: recall. Sampling has turned up 30 dead tokens in 576, because every public source ranks by liquidity and rugged pools fall off the listing entirely. Rather than compute a detection rate on a single sample and present it as a result, that figure is left blank until the daily snapshot archive matures enough to supply a real cohort. An earlier version of this section claimed "measured recall", which the benchmark file itself contradicted.
→ bench/results.md — current numbers, method, and known limits.
Why it is built this way:
Independent labels. If the benchmark labelled tokens using honeypot.is — which the engine reads — it would only measure whether VetAgent can relay honeypot.is. Labels come from realized market outcome (price/volume history) and from GoPlus, which is deliberately held out of the engine.
A runtime assertion, not a promise. The benchmark records every endpoint each side touched and fails the run if the two sets intersect. A circular benchmark is worse than none, so it is made structurally impossible rather than documented.
An ablation column. A token that already collapsed has ~zero liquidity today, so flagging it is close to tautological. Results are therefore reported twice: with all signals, and with liquidity/lifecycle signals removed. The gap is what the engine actually contributes beyond the obvious.
unknownrate reported alongside recall. A tool that answersunknownto everything has perfect recall and zero value.
python bench/build_dataset.py --limit 250 # sample + label (independent sources)
python bench/run_benchmark.py # score the local engine, write results.mdTests
python tests/test_risk.py # engine regressions, offline, real upstream snapshots
python tests/test_mcp.py # MCP protocol conformance
python tests/test_upstream_contract.py # live: asserts the JSON paths we depend on still existEvery case in tests/ is pinned to a defect that actually reached production.
The contract test earns its keep: VetAgent's worst bug was reading
simulationResult.isHoneypot when honeypot.is puts that flag in honeypotResult.
The key did not exist, the lookup returned None, it was read as False, and the
honeypot check silently passed every token it was ever asked about. No mocked test
could have caught that — only one that calls the real API and asserts the shape.
Rule for this repo: a commit that claims to fix something ships with a test that was red before it.
Architecture
Cloudflare Python Worker, no heavy dependencies.
src/
entry.py HTTP routing (entry class must be named Default)
risk.py risk engine — assess / liquidity / new_pools
mcp_server.py hand-written streamable-http MCP endpoint (JSON-RPC 2.0)
landing.html landing page
bench/ accuracy benchmark (independent labels + ablation)
tests/ regression, protocol, and upstream-contract suites
docs/ agent integration guide, handoff, MCP registry manifestThe MCP endpoint is hand-written rather than using the official mcp package: that
package pulls in pydantic's C extensions, which do not install on Cloudflare Python
Workers. Plain JSON-RPC turned out to be smaller and fully client-compatible.
Data sources
Source | Used for |
DexScreener | pairs, price, liquidity, volume, pair age |
GeckoTerminal | liquidity fallback, new/trending pools |
honeypot.is | EVM buy/sell simulation, taxes, aggregate risk, contract openness |
RugCheck | Solana rug score, mint/freeze authority, holder concentration |
GoPlus is not used by the engine — it is reserved as the benchmark's held-out oracle. Adding it to the engine requires giving the benchmark a new independent labeller first, or the accuracy numbers stop meaning anything.
Where things are written down
Four documents, one job each. If something is in two of them, one of them is wrong.
Document | Answers |
| What is this, how do I call it |
Why is it built this way, and what enforces each rule | |
Where things stand, what breaks, what to do next | |
Who pays, what the moat is, when to shut it down |
Why a change was made lives in the commit message, which is immutable and attached to the diff. Why a line of code looks odd lives in a comment next to that line. Neither gets copied into a document, because a copy rots without anyone noticing.
Scope
VetAgent reports observable on-chain risk. It is not financial advice, it does not
size positions, and it cannot see off-chain risk — team behaviour, social engineering,
or a rug executed through governance. Treat low as "no fatal signal found in the
checks that ran", never as "safe to buy".
License
Not yet chosen — see docs/HANDOFF.md.
Available Tools
3 toolsassess_token_riskAssess Token RiskARead-onlyIdempotentInspect
Safety check to run BEFORE buying, holding, or recommending a token. Returns an actionable verdict (low / medium / high / unknown), a 0-100 risk score, and the individual signals behind it. Covers: sell simulation (honeypot detection, buy/sell/transfer taxes), liquidity depth, trading-pair age, cross-chain presence, whether the contract is open source, and on Solana the mint/freeze authority and holder concentration — plus the aggregate verdicts of upstream security scanners. IMPORTANT: risk_level 'unknown' means a critical check could not be completed. It is NOT a low-risk result and must not be used to justify a trade; evidence.data_gaps lists exactly what was missing. 'confidence' measures how complete the input data was, not how safe the token is. Reports observable on-chain risk only. Not financial advice, does not size positions, and cannot see off-chain risk such as team behaviour, social engineering, or a rug executed through governance. Treat 'low' as 'no fatal signal found in the checks that ran', never as 'safe to buy'.
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | Token contract address: ERC-20 (0x + 40 hex) or Solana (base58) | |
| verbose | No | Return full upstream evidence. Off by default to save tokens. | |
| chain_hint | No | Optional chain name (ethereum / bsc / base / polygon / arbitrum / solana). Strongly recommended: Ethereum forks such as PulseChain inherit contract addresses, so the same address exists on several chains at wildly different prices. |
Output Schema
| Name | Required | Description |
|---|---|---|
| address | Yes | |
| signals | Yes | |
| evidence | No | |
| confidence | Yes | How complete the input data was — not how safe the token is. |
| risk_level | Yes | 'unknown' means a critical check could not be completed. It is NOT a low-risk result and must not justify a trade. |
| risk_score | Yes | 0-100; higher is more dangerous |
| recommendation | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations only declare the tool read-only, idempotent, and non-destructive. The description goes far beyond this by explaining the critical semantics of 'unknown', the meaning of 'confidence', the data_gaps output, and the tool's blind spots (off-chain risk, team behavior, governance rugs). This is exactly the kind of behavioral context an agent needs to avoid misusing the result.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the tool's purpose and return value, then moves through coverage, critical caveats, and limitations. It is somewhat long but earns its length given the safety-critical nature of the tool. A small amount of redundancy exists between 'Reports observable on-chain risk only' and the later 'cannot see off-chain risk' phrasing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the presence of an output schema, and the safety-sensitive use case, the description provides everything needed: what checks are performed, how to interpret non-obvious outputs like 'unknown' and 'confidence', and what the tool cannot detect. No critical operational context is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% parameter descriptions, including detailed guidance on chain_hint and verbose. The tool description does not add new parameter-level meaning, but it doesn't need to because the schema field descriptions are already thorough. This matches the baseline for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool as a pre-trade safety check that outputs a risk verdict, a 0-100 score, and supporting signals. It names the specific resource (token) and the action (assess risk), and the coverage list makes its scope unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells the agent when to use the tool: before buying, holding, or recommending a token. It also gives important when-not guidance: 'unknown' must not justify a trade, 'low' means no fatal signal found, and off-chain risks are out of scope. It does not explicitly compare against sibling tools like get_token_liquidity or find_new_hot_pools, but the usage context is otherwise clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_new_hot_poolsFind New Hot PoolsARead-onlyInspect
Scan a chain for the newest and most active trading pools, returning name, token_address, price, liquidity, 24h volume and pool age. Discovery only. New pools carry inherently high risk and appearing here is NOT a safety endorsement — pass token_address straight to assess_token_risk for anything you intend to act on.
| Name | Required | Description | Default |
|---|---|---|---|
| chain | No | Chain name, e.g. solana / ethereum / base / bsc | solana |
| limit | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| chain | Yes | |
| count | Yes | How many pools are in `pools`. NOT how many were examined -- that is `scanned`. This field reported the fetched total beside a shorter list until 2026-09-08, so a caller reading it believed it had seen twenty pools when it had three. |
| pools | Yes | |
| network | Yes | The upstream's own name for the chain, which differs from `chain` (eth vs ethereum). |
| scanned | Yes | How many distinct pools were examined before the limit was applied. Always >= count. |
| sources_ok | Yes | Which of the two upstream listings answered. Both means a full scan. |
| served_stale | No | Present only when an upstream was unreachable and cached data was used. Each entry names the source and its age in seconds. |
| sources_failed | Yes | Which listings did NOT answer. A non-empty array means this is a PARTIAL scan: a smaller `scanned` here is a coverage gap, not a quiet market. Treat it as missing information, not as absence. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/open-world/non-destructive behavior, and the description adds the important caveat that appearing here is 'NOT a safety endorsement' and that new pools carry high risk. This is valuable non-obvious context beyond the annotations, though it doesn't elaborate on ordering, freshness, or rate-limit behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two crisp sentences, front-loaded with the action and return fields, with the risk caveat placed second. No wasted words and no repetition of schema or annotation data.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter, zero-required discovery tool with an output schema, the description is complete: it states the purpose, output scope, risk posture, and the follow-up action. The only minor omission is explicit guidance on get_token_liquidity, which is a usage-guideline nuance rather than a completeness gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description conveys no input-parameter meaning; chain is explained in the schema and limit is only self-evident from its name plus min/max/default constraints. With 50% schema coverage, the description doesn't add enough to lift parameter understanding beyond what the schema already offers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete action ('Scan a chain') and a specific resource ('newest and most active trading pools'), and lists the exact return fields. It also frames itself as 'Discovery only' and routes to assess_token_risk, distinguishing it from the risk-assessment sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The 'Discovery only' line and the explicit instruction to send token_address to assess_token_risk for anything you intend to act on give clear when/when-not guidance. However, it never addresses get_token_liquidity as the targeted single-token liquidity lookup, so the sibling comparison is incomplete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_token_liquidityGet Token LiquidityARead-onlyIdempotentInspect
Liquidity snapshot for a token's primary trading pair: price, 24h volume, pair count and the chains it trades on. Check 'status' before using the numbers. 'ok' means real data. 'unavailable' means the upstream request failed, which does NOT mean the token has no liquidity. 'not_found' means no trading pair exists for this address at all. 'unpriced' means pairs exist but no source has costed them, so liquidity_usd is null and the depth is unknown -- this is NOT a report of zero liquidity. 'drained' means every pool on the token's own chain reports its depth and every one is empty: there is nothing to sell into.
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | Token contract address | |
| chain_hint | No | Optional chain name; disambiguates forks that share addresses |
Output Schema
| Name | Required | Description |
|---|---|---|
| chains | No | |
| status | Yes | |
| address | Yes | |
| price_usd | No | |
| pairs_total | No | |
| served_stale | No | Present only when an upstream was unreachable and this answer used cached data. Each entry names the source and how many seconds old it was. |
| liquidity_usd | No | |
| volume_24h_usd | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description richly discloses behavior beyond the annotations: it explains failure modes, what null liquidity means, that 'unavailable' does not indicate zero liquidity, and what 'drained' implies for tradability. This is exactly the interpretive context an agent needs and goes well beyond the readOnlyHint/idempotentHint annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact but information-dense, with the core purpose front-loaded and status semantics following in a structured, readable sequence. Every sentence adds value by preventing a specific misinterpretation, so the length is justified and efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity, full schema coverage, rich annotations, and presence of an output schema, the description is complete. It covers the main data returned, the critical status field, edge cases, and the meaning of null values — everything an agent needs to correctly interpret results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description does not add new parameter-level detail beyond the schema, but the schema already documents 'address' and 'chain_hint' sufficiently, including the fork-disambiguation purpose of chain_hint. No compensation is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: "Liquidity snapshot for a token's primary trading pair," and lists concrete outputs such as price, 24h volume, pair count, and chains. This clearly distinguishes it from sibling tools like find_new_hot_pools or assess_token_risk, which target discovery and risk assessment rather than current liquidity measurement.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear operational guidance: check 'status' before trusting numbers, and interprets each status value so the agent knows how to act on 'unavailable', 'not_found', 'unpriced', and 'drained'. It does not explicitly name sibling alternatives or state when to prefer them, but it provides strong context for correct usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
3 tool updates
v0.1.0- First observed
assess_token_risk - First observed
find_new_hot_pools - First observed
get_token_liquidity
TDQS
Scored across 3 tools
Each tool targets a distinct stage in the token-vetting workflow: discovery of new pools, risk assessment, and liquidity snapshot. Although assess_token_risk and get_token_liquidity both touch liquidity, their purposes and outputs are clearly separated (verdict vs. market data).
All tool names follow a consistent verb_noun snake_case pattern: find_new_hot_pools, assess_token_risk, get_token_liquidity. The verbs are descriptive and the object nouns clearly identify the target resource.
Three tools is well-scoped for a focused vetting agent: one discovery entry point, one risk-check entry point, and one liquidity-data entry point. No tool is redundant and none is missing from the core workflow.
The set covers the full discovery-to-risk-to-liquidity pipeline with no dead ends. Minor gaps exist, such as no historical-tracking or token-profile/summary tool beyond the risk check, but agents can work around them.
Maintenance
Related MCP Connectors
Pre-trade token safety checks for AI agents on Solana and Base. x402 USDC per call, no key.
Honeypot detection & token risk scan for ERC-20s. Risk score 0-100, tax, source verification.
Solana token safety for AI agents — rug-pull, honeypot & Token-2022 trap detection before you buy.
Instant rug-check for any EVM or Solana token, distilled to one clear 0-10 risk verdict.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceToken safety oracle for AI agents. Honeypot detection, 17 scam pattern checks, LP lock verification across 6 EVM chains. Score 0-100 with risk flags. ERC Token Safety Score standard.1MIT
- AlicenseAqualityBmaintenanceQuick-scan a smart contract for rug, honeypot, or centralization risk before sending funds. It combines verified source, live on-chain state, and heuristic Solidity analysis to return a SAFE/CAUTION/HIGH-RISK verdict.1MIT
- AlicenseAqualityBmaintenanceOn-chain rug-pull & honeypot risk screen for ERC-20 tokens, providing a SAFE / CAUTION / HIGH-RISK verdict based on live public RPC reads.2MIT
- AlicenseNot gradedqualityBmaintenanceChecks token contract safety for honeypot, tax, proxy, blacklist, ownership risks, and returns a risk score, enabling rug-pull protection for agents via pay-per-call x402 micropayments.MIT