Skip to main content
Glama
abetoots

tokensift

Official
by abetoots
README.md
# tokensift

Read-only token due-diligence over keyless public APIs, as one Python package that is both an importable library and a stdio MCP server. Six tools, one envelope, no trading, no secrets.

| Tool | Upstream | Chains |
|---|---|---|
| `honeypot_check` | honeypot.is buy/sell simulation | ethereum, bsc, base |
| `rugcheck_report` | rugcheck.xyz risk report | solana |
| `dex_search` | DexScreener search | all |
| `dex_token_pairs` | DexScreener pools for a token | ethereum, bsc, base, solana |
| `dex_pair` | DexScreener pair snapshot | ethereum, bsc, base, solana |
| `jupiter_prices` | Jupiter Price v3 (lite host) | solana |

Every call returns the same envelope (also each tool's `outputSchema`):

```json
{"ok": true, "source": {"provider": "dexscreener", "url": "…exact GET…", "chain": "solana"},
 "fetched_at": "2026-09-06T12:00:00Z", "cache": {"hit": false, "ttl_s": 30},
 "summary": {…}, "raw": null, "raw_truncated": false, "error": null}
```

On failure `ok` is false, `summary` is null, and `error` carries `type` (`invalid_input | not_found | rate_limited | upstream_unavailable | upstream_changed`), `message`, optional `retry_after_s`, and `advice` written for the model. The MCP tool returns the same envelope with `isError=true`.

## Library

```python
from tokensift import Tokensift

async with Tokensift() as ts:
    env = await ts.honeypot_check("base", "0x…")
    if env.ok:
        print(env.summary["is_honeypot"], env.summary["sell_tax"])
```

## MCP server

```bash
uvx --from /path/to/tokensift tokensift-mcp      # stdio
```

See `docs/REGISTRATION.md` for Claude Code, Codex CLI, and Gemini CLI entries and the Inspector smoke test.

## Behavior that matters

- Validation before network: chain aliases (`eth`, `1`, `bnb`, `56`, `8453`, `sol`) and address formats are checked first; bad input never hits an upstream.
- Per-provider token buckets (`TOKENSIFT_RPM_*`). When a bucket is empty the call waits up to `TOKENSIFT_LIMITER_WAIT_S` (5 s) and then returns `rate_limited` with `retry_after_s`. `rate_limited` and `upstream_unavailable` are never cached; `invalid_input` and `not_found` are cached for `TOKENSIFT_NEG_CACHE_TTL_S` (10 s).
- Successful responses are cached for `TOKENSIFT_CACHE_TTL_S` (30 s) keyed by provider, endpoint, and normalized params.
- One retry on 429/502/503/504 honoring `Retry-After`, capped at 5 s. Timeouts and 401/403 become `upstream_unavailable` (403 advice names the datacenter-IP possibility).
- `raw` is returned only with `verbose=true`, capped at `TOKENSIFT_RAW_MAX_BYTES` (32 kB) with `raw_truncated=true` when cut.
- Logging goes to stderr only; stdout is the MCP wire.
- Dual-era: the server answers both a legacy `initialize` handshake and modern `2026-07-28` per-request metadata (tested over a real pipe).

## Development

```bash
uv sync
uv run pytest                 # unit + client + server + contract + stdio (no network)
uv run pytest -m live         # one real call per upstream; run weekly
uv run python scripts/record_fixtures.py   # refresh recorded upstream fixtures
npx -y @modelcontextprotocol/inspector --cli ./scripts/serve-stdio.sh --method tools/list   # flag-free launcher: the inspector cli eats `--from`
```

Stack (pinned 2026-09-06): `fastmcp>=4.0,<4.1` on `mcp` 2.x (spec `2026-07-28`), `httpx2`, `pydantic-settings`, `aiolimiter`, `cachetools`. Python 3.12 via `uv`.

### T0 spike findings (2026-09-06)

- `fastmcp.tools.ToolResult(content, structured_content, meta, is_error)` accepts `is_error` with structured content, so errors are returned as results, never raised (a raised exception becomes a JSON-RPC error in `mcp` 2.x).
- `mcp.server.runner` drives `serve_dual_era_loop`; a legacy `initialize` is handled inline and negotiated from `HANDSHAKE_PROTOCOL_VERSIONS`. `tests/test_stdio.py` proves both handshakes over a pipe.
- The `Envelope` field `ok` forbids a classmethod of the same name; factories are `Envelope.success` / `Envelope.failed`.
- MCP Inspector CLI v2 parses flags anywhere on its command line, so a target like `uvx --from … tokensift-mcp` loses `--from` and reports "Connection closed". `scripts/serve-stdio.sh` is a flag-free launcher for the Inspector; the three CLIs pass args through correctly and use `uvx` directly.

TDQS

A4.2/5.0

Scored across 6 tools

Disambiguation4/5

Tools are mostly distinct: honeypot_check and rugcheck_report clearly separate EVM vs Solana safety checks; dex_search, dex_token_pairs, and dex_pair cover different granularities of pair data. Minor overlap exists between dex_search and dex_token_pairs, but descriptions clarify when each is appropriate.

Naming Consistency5/5

All tool names use consistent snake_case and follow a predictable pattern: domain-specific prefix (dex_, jupiter_, honeypot_, rugcheck_) plus a clear noun or verb. The naming scheme makes the tool's function obvious and uniform across the set.

Tool Count5/5

Six tools is a well-scoped size for a token research and safety-checking server. Each tool covers a distinct aspect of the workflow (search, pair lookup, pair snapshot, safety verification, pricing) without redundancy or bloat.

Completeness4/5

The core workflow (find token, verify pair, assess safety, get price) is covered. Minor gaps exist: no EVM-specific price source analogous to jupiter_prices, and dex_token_pairs only queries one chain at a time, requiring multiple calls for cross-chain analysis.

Maintenance

ActivityMaintained
ResponsivenessNo issues