dev.vetagent/vetagent
Pre-trade safety check for AI agents: honeypot, tax, liquidity and rug risk before you buy.
Open source Repository Open in the app JSON README (API)
About
Pre-trade safety check for AI agents: honeypot, tax, liquidity and rug risk before you buy.
Details
- Kind
- MCP servers
- Topic
- Finance & crypto
- Publisher
- dev.vetagent
- Origin
- official
- Category
- ferramentas
- Transport
- http
- Version
- 0.2.0
- Last push
- 2026-09-07T18:52:41Z
- Repository state
- ativo
- Language
- Python
- License
- MIT
- Added
- 2026-09-03 18:00:56
- Updated
- 2026-09-03 18:00:56
- Origin id
dev.vetagent/vetagent
README
# 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**
```jsonc
// 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:
```bash
claude mcp add --transport http vetagent https://vetagent.dev/mcp
```
Cline, Cursor, Claude Desktop, or any other MCP client — add to the MCP config:
```json
{
"mcpServers": {
"vetagent": {
"type": "http",
"url": "https://vetagent.dev/mcp"
}
}
}
```
Confirm it worked — three tools should come back:
```bash
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`](llms-install.md) for the version written for an agent doing the
installing, and [`docs/AGENT-INTEGRATION.md`](docs/AGENT-INTEGRATION.md) for how to use
the output once connected.
---
## `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 |
|---|---|
| `assess_token_risk(address, chain_hint?, verbose?)` | Full risk profile: sellability simulation, liquidity depth, pair age, holder concentration, contract permissions, upstream aggregate verdicts |
| `get_token_liquidity(address, chain_hint?)` | Liquidity snapshot for the primary pair, with an explicit `status` so "upstream failed" is distinguishable from "no pools exist" |
| `find_new_hot_pools(chain?, limit?)` | 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`](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 legitimate centralised assets flagged high.
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`](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.
- **`unknown` rate reported alongside recall.** A tool that answers `unknown` to
everything has perfect recall and zero value.
```bash
python bench/build_dataset.py --limit 250 # sample + label (independent sources)
python bench/run_benchmark.py # score the local engine, write results.md
```
---
## Tests
```bash
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 exist
```
Every 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 manifest
```
The 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 |
|---|---|
| `README.md` (this file) | What is this, how do I call it |
| [`docs/DECISIONS.md`](docs/DECISIONS.md) | Why is it built this way, and **what enforces each rule** |
| [`docs/HANDOFF.md`](docs/HANDOFF.md) | Where things stand, what breaks, what to do next |
| [`docs/STRATEGY.md`](docs/STRATEGY.md) | 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`.