cabal-hunter-mcp 0.1.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +4 -0
- package/.mcp.json +8 -0
- package/Dockerfile +17 -0
- package/LICENSE +1 -1
- package/README.md +398 -49
- package/demo/python_demo.py +74 -0
- package/demo/screenshot.png +0 -0
- package/demo/typescript_demo.ts +90 -0
- package/example_bot.py +84 -0
- package/glama.json +4 -0
- package/package.json +13 -38
- package/server/index.mjs +125 -0
- package/index.js +0 -95
package/.dockerignore
ADDED
package/.mcp.json
ADDED
package/Dockerfile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Cabal-Hunter local stdio MCP server.
|
|
2
|
+
# Used by Glama (and any MCP host) to build + run the server for automated
|
|
3
|
+
# tool discovery and quality checks. Requires no secrets — the free tier works
|
|
4
|
+
# out of the box; paid scans use x402 at call time.
|
|
5
|
+
FROM node:20-slim
|
|
6
|
+
|
|
7
|
+
WORKDIR /app
|
|
8
|
+
|
|
9
|
+
# Install deps first for layer caching.
|
|
10
|
+
COPY package.json ./
|
|
11
|
+
RUN npm install --omit=dev --no-audit --no-fund
|
|
12
|
+
|
|
13
|
+
# Copy the server.
|
|
14
|
+
COPY server ./server
|
|
15
|
+
|
|
16
|
+
# MCP servers speak JSON-RPC over stdio.
|
|
17
|
+
ENTRYPOINT ["node", "server/index.mjs"]
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,95 +1,444 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Solana Safe Sniper — MCP Template
|
|
2
2
|
|
|
3
|
-
**On-chain Solana cabal & rug detection as an MCP server.** One tool — `check_cabal_risk` — scans any Solana token mint *before your agent buys* and returns an **Exit-Liquidity Risk** verdict (`SAFE | REVIEW | AVOID`), a 0–100 cabal score, funding-cluster detection, same-block Jito-bundle detection, coordinated-dump detection, serial-rug **deployer history** ("launched 14, 13 dead"), and a Solana-native **honeypot** check (freeze authority + Token-2022 traps). Every flag links to its on-chain evidence transaction.
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/cabal-hunter-mcp)
|
|
6
3
|
[](https://api.cabal-hunter.com/mcp)
|
|
7
4
|
[](https://api.cabal-hunter.com)
|
|
8
|
-
[](https://api.cabal-hunter.com/demo)
|
|
6
|
+
[](https://api.cabal-hunter.com/api/info)
|
|
7
|
+
[](https://insiders.vscode.dev/redirect/mcp/install?name=cabal-hunter&config=%7B%22type%22%3A%20%22http%22%2C%20%22url%22%3A%20%22https%3A%2F%2Fapi.cabal-hunter.com%2Fmcp%22%7D)
|
|
8
|
+
[](https://cursor.com/install-mcp?name=cabal-hunter&config=eyJ1cmwiOiAiaHR0cHM6Ly9hcGkuY2FiYWwtaHVudGVyLmNvbS9tY3AifQ==)
|
|
9
|
+
[](https://github.com/paulf280-ui/plugin-cabal-hunter)
|
|
9
10
|
[](LICENSE)
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
> 🌐 **Available in 9 languages:** [English](https://api.cabal-hunter.com/) · [Español](https://api.cabal-hunter.com/es) · [Português](https://api.cabal-hunter.com/pt) · [Français](https://api.cabal-hunter.com/fr) · [Deutsch](https://api.cabal-hunter.com/de) · [Nederlands](https://api.cabal-hunter.com/nl) · [中文](https://api.cabal-hunter.com/zh) · [日本語](https://api.cabal-hunter.com/ja) · [한국어](https://api.cabal-hunter.com/ko)
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+

|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
> Stop your AI trading agents getting rugged by coordinated wallet cabals.
|
|
17
|
+
> Drop-in template for Claude Code, Cursor, and ElizaOS.
|
|
18
|
+
|
|
19
|
+
**▶ Try it now: [live 3D holder map of any Solana token →](https://api.cabal-hunter.com/demo)** — no signup.
|
|
20
|
+
|
|
21
|
+
**Cabal-Hunter is a free on-chain Solana token safety scanner and rug checker.** It detects coordinated wallet cabals, same-block Jito bundle buys, serial-launcher deployers and coordinated dumps on any Solana mint (pump.fun, PumpSwap, Raydium) — and answers the one question that matters before you ape: *are you the exit liquidity?* Use it via **MCP** (Claude, Cursor, ElizaOS), a **REST API**, or a free **interactive 3D holder map**.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## The Problem
|
|
26
|
+
|
|
27
|
+
Your autonomous trading agent is reading rug.check scores, liquidity locks, and contract audits.
|
|
28
|
+
|
|
29
|
+
**None of that catches a cabal.**
|
|
30
|
+
|
|
31
|
+
A handful of wallets take the bottom of a launch, the chart looks clean — contract fine, LP burned, everything green — and then they sell into whoever bought after them. You are the exit liquidity.
|
|
32
|
+
|
|
33
|
+
This template integrates **[Cabal-Hunter](https://api.cabal-hunter.com)** as a pre-trade safety check, so your agent can see who is holding and who has already positioned to sell before it signs a swap.
|
|
34
|
+
|
|
35
|
+
> **A note on what we do and don't claim.** This README used to open with "15 fresh wallets funded from the same master wallet, accumulating 25-40% of supply." We went looking for that pattern and could not find it. Tracing 323 pump.fun launches at the bonding curve turned up **zero coordinated funding clusters**, and showed why: the median launch has about **five successful buyers**, because on one representative token **1,260 of the curve's 1,266 transactions failed**. Launch capture is a sniper *race* between competing bots, not a quiet cabal. The detection layers below are the ones we can actually evidence — holder concentration, same-block bundles, coordinated selling and deployer history. The pre-launch funding tracer was withdrawn; the [full write-up is here](https://github.com/paulf280-ui/cabal-hunter-mcp#a-note-on-the-withdrawn-trace_funding-tool).
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## The one question it answers: are YOU the exit liquidity?
|
|
40
|
+
|
|
41
|
+
The classic pump.fun exit-liquidity setup: wallets positioned before the crowd take the bottom of a launch, then dump on the retail (and bots) that pile in after. Cabal-Hunter's headline output is a single **Exit-Liquidity Risk** verdict (`LOW | ELEVATED | HIGH`) that synthesises every signal below into the only thing that matters before you sign a swap: *are the insiders positioned to dump on you?*
|
|
42
|
+
|
|
43
|
+
## What Cabal-Hunter Does
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Token mint address
|
|
47
|
+
↓
|
|
48
|
+
0. EXIT-LIQUIDITY RISK — the headline verdict. Synthesises the layers
|
|
49
|
+
below (bundle, concentration, shared funder, coordinated dump, serial-
|
|
50
|
+
rug dev) into LOW | ELEVATED | HIGH: are insiders set up to dump on
|
|
51
|
+
a buyer? The one number a trading agent needs.
|
|
52
|
+
↓
|
|
53
|
+
1. HOLDER FUNDING LINEAGE — the CURRENT top holders walked back: were
|
|
54
|
+
they seeded by the same wallet? Only System-owned accounts count as
|
|
55
|
+
a funder, so pools, vaults and routers can never be mistaken for a
|
|
56
|
+
person (that mistake is exactly what got our pre-launch tracer
|
|
57
|
+
withdrawn). Every cluster carries evidence_txs[] — the actual
|
|
58
|
+
funding transactions on Solscan. This layer is genuinely rare to
|
|
59
|
+
fire; treat a hit as significant and its absence as no evidence
|
|
60
|
+
either way.
|
|
61
|
+
↓
|
|
62
|
+
2. SAME-BLOCK BUNDLE DETECTION — holders whose token accounts were
|
|
63
|
+
created in the EXACT same slot bought in one Jito bundle. Catches
|
|
64
|
+
stealth launches that route funding through intermediaries to
|
|
65
|
+
evade layer 1. Returned as `time_sync: true`.
|
|
66
|
+
↓
|
|
67
|
+
3. COORDINATED DUMP DETECTION — ≥2 holders that SOLD a meaningful chunk
|
|
68
|
+
(≥25% of their bag each) in the EXACT same block — a cabal exiting in
|
|
69
|
+
real time. `coordinated_exit: true`, with sold_pct = % of supply
|
|
70
|
+
dumped and the sell transactions linked. Same-slot + meaningful-size +
|
|
71
|
+
distinct wallets = near-zero false positives.
|
|
72
|
+
↓
|
|
73
|
+
4. DEV TRACK RECORD — the creator wallet is resolved on-chain (bonding
|
|
74
|
+
curve pre-graduation, pump-amm pool after — works on any age token),
|
|
75
|
+
and their full launch history is pulled WITH THE PEAK MARKET CAP each
|
|
76
|
+
past token hit. A dead-count alone hides a pump-and-dump: a dev whose
|
|
77
|
+
tokens all died at $4k is a nobody, but one who ran a token to $728k
|
|
78
|
+
then dumped it to dust has done it to holders before. Reputation:
|
|
79
|
+
SERIAL_RUGGER | DEAD_ON_ARRIVAL | MIXED | PROVEN, with per-launch
|
|
80
|
+
peak → now (paid tier returns the full launches[] array).
|
|
81
|
+
↓
|
|
82
|
+
5. CEX-NOISE FILTER — holders funded from a shared exchange or
|
|
83
|
+
high-volume infra wallet are NOT a cabal. They're excluded from the
|
|
84
|
+
score and surfaced transparently in filtered_clusters[], so you never
|
|
85
|
+
get a false positive from people who just withdrew from Binance.
|
|
86
|
+
↓
|
|
87
|
+
6. HONEYPOT CHECK (Solana-native) — one RPC read of the mint account:
|
|
88
|
+
is the FREEZE authority live (issuer can freeze your tokens — the
|
|
89
|
+
sell-block lever)? is the MINT authority live (supply can be
|
|
90
|
+
inflated)? any Token-2022 traps (transfer fees up to 100%, transfer
|
|
91
|
+
hooks that block sells, permanent-delegate clawback)? On Solana a
|
|
92
|
+
"honeypot" is built from these — tokens have no per-token contract
|
|
93
|
+
code to audit. Returns `honeypot_risk: LOW | HIGH` +
|
|
94
|
+
`freeze_authority_revoked` / `mint_authority_revoked` /
|
|
95
|
+
`token2022_risks[]`.
|
|
96
|
+
↓
|
|
97
|
+
Returns: Cabal Score (0-100) + cluster map + deployer verdict
|
|
98
|
+
+ honeypot verdict + on-chain receipts + hard verdict
|
|
17
99
|
```
|
|
18
100
|
|
|
19
|
-
|
|
101
|
+
The deployer layer is the one cabals can't dodge: **wallets rotate, deployers leave a paper trail.** A response of `"deployer": {"reputation": "SERIAL_RUGGER", "tokens_launched": 22, "best_peak_usd": 728432, "pump_and_dumps": 2}` shows the dev's full track record before the first candle — including whether this "dead" dev has quietly run tokens to six figures and dumped them on holders before. (Honest context: most prolific pump.fun creators have high dead-token rates, so this signal is capped — it flags a token for review but never drives a HIGH verdict on its own.)
|
|
20
102
|
|
|
21
|
-
|
|
103
|
+
**Receipts, not magic.** Every cluster and red flag links to the underlying Solscan transaction (`evidence_txs[]`, `holders[].funding_tx`) — verify the trail yourself instead of trusting a score.
|
|
104
|
+
|
|
105
|
+
**Response in <100ms** on pre-indexed tokens — every pump.fun graduation is scanned and cached automatically as it happens.
|
|
106
|
+
|
|
107
|
+
**Free tier: 250 scans/month per IP.** Then $0.001 USDC per scan — priced at cost (it covers the Helius RPC calls behind each live on-chain trace), paid natively on Solana. No account. No API key. No subscription.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Quick Start
|
|
112
|
+
|
|
113
|
+
### 1. Claude Code / Claude Desktop
|
|
114
|
+
|
|
115
|
+
Add to your MCP config (`~/.claude/mcp.json` or project `.mcp.json`):
|
|
22
116
|
|
|
23
117
|
```json
|
|
24
118
|
{
|
|
25
119
|
"mcpServers": {
|
|
26
120
|
"cabal-hunter": {
|
|
27
|
-
"
|
|
28
|
-
"args": ["-y", "cabal-hunter-mcp"]
|
|
121
|
+
"url": "https://api.cabal-hunter.com/mcp"
|
|
29
122
|
}
|
|
30
123
|
}
|
|
31
124
|
}
|
|
32
125
|
```
|
|
33
126
|
|
|
34
|
-
|
|
127
|
+
That's it. Claude will now call `check_cabal_risk` automatically when you ask it to analyse a Solana token.
|
|
128
|
+
|
|
129
|
+
**Example prompt:**
|
|
130
|
+
> "Before we buy into this token, check if there are any coordinated wallets: `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`"
|
|
131
|
+
|
|
132
|
+
Claude calls the tool, pays $0.001 USDC from your connected wallet, and returns the full analysis.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### 2. Cursor
|
|
137
|
+
|
|
138
|
+
Add to `.cursor/mcp.json` in your project root:
|
|
35
139
|
|
|
36
140
|
```json
|
|
37
|
-
{
|
|
141
|
+
{
|
|
142
|
+
"mcpServers": {
|
|
143
|
+
"cabal-hunter": {
|
|
144
|
+
"url": "https://api.cabal-hunter.com/mcp"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### 3. ElizaOS (with automatic x402 payment)
|
|
153
|
+
|
|
154
|
+
If you're using ElizaOS with `@hugen/plugin-x402-solana`, payment is handled automatically. Add to your agent config:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"plugins": ["@hugen/plugin-x402-solana"],
|
|
159
|
+
"mcpServers": {
|
|
160
|
+
"cabal-hunter": {
|
|
161
|
+
"url": "https://api.cabal-hunter.com/mcp"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Your agent will call `check_cabal_risk(mintAddress)` before any swap and abort if `cabalScore >= 35` or `isControlled === true`.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### 4. Direct REST API
|
|
172
|
+
|
|
173
|
+
For headless scripts, custom bots, or any language. **The first 250 calls/month are free — no key, no signup.** Just call it:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
curl "https://api.cabal-hunter.com/api/scan-cabal?mintAddress=YOUR_MINT_ADDRESS"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
You get the full analysis back immediately, with `free_queries_remaining` so you always know where you stand. Machine-readable contract: [`/openapi.json`](https://api.cabal-hunter.com/openapi.json).
|
|
180
|
+
|
|
181
|
+
Once the free tier is used up, calls are $0.001 USDC via x402 — your agent just pays, no billing setup:
|
|
182
|
+
|
|
183
|
+
**Step 1 — Request analysis (get payment instructions):**
|
|
184
|
+
```bash
|
|
185
|
+
curl -X POST https://api.cabal-hunter.com/api/scan-cabal \
|
|
186
|
+
-H "Content-Type: application/json" \
|
|
187
|
+
-d '{"mintAddress": "YOUR_MINT_ADDRESS"}'
|
|
38
188
|
```
|
|
39
189
|
|
|
40
|
-
|
|
190
|
+
Response (HTTP 402):
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"error": "payment_required",
|
|
194
|
+
"payment": {
|
|
195
|
+
"recipient": "ATYjZ1kWoHWhj74umGJ8wFqUeW1yeSGBbLi1UQpahPxt",
|
|
196
|
+
"amount_usdc": 0.001,
|
|
197
|
+
"memo_required": "ch-xxxx-xxxx-xxxx",
|
|
198
|
+
"instructions": "Send 0.001 USDC with this memo, then resubmit with X-Payment-Signature header"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
41
202
|
|
|
42
|
-
|
|
203
|
+
**Step 2 — Pay & resubmit:**
|
|
204
|
+
```bash
|
|
205
|
+
curl -X POST https://api.cabal-hunter.com/api/scan-cabal \
|
|
206
|
+
-H "Content-Type: application/json" \
|
|
207
|
+
-H "X-Payment-Signature: YOUR_TX_SIGNATURE" \
|
|
208
|
+
-d '{"mintAddress": "YOUR_MINT_ADDRESS"}'
|
|
209
|
+
```
|
|
43
210
|
|
|
44
|
-
|
|
211
|
+
**Response (HTTP 200):**
|
|
212
|
+
```json
|
|
45
213
|
{
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
214
|
+
"mint": "YOUR_MINT",
|
|
215
|
+
"token_name": "EXAMPLE",
|
|
216
|
+
"risk": "HIGH",
|
|
217
|
+
"cabal_score": 72.4,
|
|
218
|
+
"is_controlled": true,
|
|
219
|
+
"time_sync": true,
|
|
220
|
+
"verdict": "AVOID — 4 wallets bought in the EXACT same block (bundled launch), controlling 34.1% of supply. DEPLOYER ALERT: this creator has launched 14 tokens, 13 of 13 checked are dead (100%).",
|
|
53
221
|
"coordinated_clusters": [
|
|
54
|
-
{
|
|
222
|
+
{
|
|
223
|
+
"type": "funding",
|
|
224
|
+
"master_full": "FvbEKF...9RUg",
|
|
225
|
+
"master_short": "FvbEKF…9RUg",
|
|
226
|
+
"wallet_count": 4,
|
|
227
|
+
"combined_pct": 34.1,
|
|
228
|
+
"risk": "HIGH",
|
|
229
|
+
"evidence_txs": ["4Y8auc5G...", "2XQx9LFv...", "AAbJ7rej..."]
|
|
230
|
+
}
|
|
55
231
|
],
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
232
|
+
"filtered_clusters": [
|
|
233
|
+
{
|
|
234
|
+
"funder_label": "high-volume wallet",
|
|
235
|
+
"master_short": "43ViqZ…Z6iy",
|
|
236
|
+
"wallet_count": 2,
|
|
237
|
+
"combined_pct": 4.4
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"deployer": {
|
|
241
|
+
"creator": "5TbRN6...full address...",
|
|
242
|
+
"creator_short": "5TbRN6…2TGC",
|
|
243
|
+
"tokens_launched": 14,
|
|
244
|
+
"dead": 13,
|
|
245
|
+
"sampled": 13,
|
|
246
|
+
"dead_pct": 100.0,
|
|
247
|
+
"verdict": "SERIAL_LAUNCHER"
|
|
248
|
+
},
|
|
249
|
+
"holders": [
|
|
250
|
+
{ "rank": 1, "address": "...", "pct": 12.4, "cluster_id": 0, "funding_tx": "4Y8auc5G..." }
|
|
251
|
+
],
|
|
252
|
+
"wallets_checked": 12,
|
|
253
|
+
"analysis_time_ms": 487,
|
|
254
|
+
"source": "real_time"
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 5. Run the MCP server locally (Docker / Node)
|
|
259
|
+
|
|
260
|
+
**Fastest — no clone needed:** `npx cabal-hunter-mcp` — the same stdio server, published to npm ([cabal-hunter-mcp](https://github.com/paulf280-ui/cabal-hunter-mcp) · [npm](https://www.npmjs.com/package/cabal-hunter-mcp)). Or run it from this repo:
|
|
261
|
+
|
|
262
|
+
Prefer to run the connector yourself instead of hitting the hosted `/mcp`
|
|
263
|
+
endpoint? This repo ships a thin **stdio MCP server** that exposes
|
|
264
|
+
`check_cabal_risk(mintAddress)` and proxies to the Cabal-Hunter API (free tier
|
|
265
|
+
works with no key; paid scans use x402 at call time):
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# Node 18+
|
|
269
|
+
npm install
|
|
270
|
+
node server/index.mjs
|
|
271
|
+
|
|
272
|
+
# or Docker
|
|
273
|
+
docker build -t cabal-hunter-mcp .
|
|
274
|
+
docker run -i cabal-hunter-mcp
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Then point any MCP client at the local command:
|
|
278
|
+
|
|
279
|
+
```json
|
|
280
|
+
{
|
|
281
|
+
"mcpServers": {
|
|
282
|
+
"cabal-hunter": {
|
|
283
|
+
"command": "node",
|
|
284
|
+
"args": ["server/index.mjs"]
|
|
285
|
+
}
|
|
286
|
+
}
|
|
61
287
|
}
|
|
62
288
|
```
|
|
63
289
|
|
|
64
|
-
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Integrate into Your Trading Logic
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
import requests
|
|
296
|
+
|
|
297
|
+
def is_safe_to_buy(mint_address: str, payment_sig: str) -> bool:
|
|
298
|
+
"""Returns True if token passes cabal check."""
|
|
299
|
+
resp = requests.post(
|
|
300
|
+
"https://api.cabal-hunter.com/api/scan-cabal",
|
|
301
|
+
json={"mintAddress": mint_address},
|
|
302
|
+
headers={"X-Payment-Signature": payment_sig}
|
|
303
|
+
)
|
|
304
|
+
if resp.status_code != 200:
|
|
305
|
+
return False # fail-safe: don't buy on error
|
|
306
|
+
data = resp.json()
|
|
307
|
+
# Block on: coordinated control, high score, bundled launch,
|
|
308
|
+
# or a deployer with a history of dead tokens
|
|
309
|
+
deployer_verdict = (data.get("deployer") or {}).get("verdict", "UNKNOWN")
|
|
310
|
+
return (
|
|
311
|
+
not data.get("is_controlled")
|
|
312
|
+
and data.get("cabal_score", 100) < 35
|
|
313
|
+
and not data.get("time_sync")
|
|
314
|
+
and deployer_verdict not in ("SERIAL_RUGGER", "SERIAL_LAUNCHER", "POOR_TRACK_RECORD")
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# In your bot's buy logic:
|
|
318
|
+
if is_safe_to_buy(token_mint, my_payment_sig):
|
|
319
|
+
execute_swap(token_mint, sol_amount)
|
|
320
|
+
else:
|
|
321
|
+
print(f"Cabal detected — skipping {token_mint}")
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Interactive 3D Holder Map (Free)
|
|
327
|
+
|
|
328
|
+
See exactly what the analysis found. Every real holder is a faceted crystal sized by its share of supply; wallets in the same cluster are joined by light beams, and the liquidity pool and locked supply sit apart in a wireframe vault because they cannot be sold. Drag to rotate, hover for the wallet, click through to Solscan:
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
https://api.cabal-hunter.com/map?mint=ANY_SOLANA_MINT
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Free to view, in 9 languages. Share this URL when you catch a rug. Every crystal is clickable and links to Solscan for deep-dive research.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Cabal-Hunter everywhere
|
|
339
|
+
|
|
340
|
+
Same detection engine, wherever your stack lives:
|
|
65
341
|
|
|
66
|
-
|
|
342
|
+
- **`npx cabal-hunter-mcp`** — standalone MCP server for Claude · Cursor · VS Code · any MCP client: [cabal-hunter-mcp](https://github.com/paulf280-ui/cabal-hunter-mcp) · [npm](https://www.npmjs.com/package/cabal-hunter-mcp)
|
|
343
|
+
- **ElizaOS plugin:** `npm i elizaos-plugin-cabal-hunter` — [plugin-cabal-hunter](https://github.com/paulf280-ui/plugin-cabal-hunter) · [npm](https://www.npmjs.com/package/elizaos-plugin-cabal-hunter)
|
|
344
|
+
- **REST API + OpenAPI:** [api.cabal-hunter.com](https://api.cabal-hunter.com) · [/openapi.json](https://api.cabal-hunter.com/openapi.json)
|
|
67
345
|
|
|
68
|
-
|
|
346
|
+
---
|
|
69
347
|
|
|
70
348
|
## Pricing
|
|
71
349
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
350
|
+
**First 250 scans every month are free** — per IP, no signup, no API key.
|
|
351
|
+
|
|
352
|
+
After that, pick whatever matches how hard your bot works (priced at cost — it covers the Helius RPC behind each live on-chain trace):
|
|
353
|
+
|
|
354
|
+
| Plan | Price | What you get |
|
|
355
|
+
|------|-------|--------------|
|
|
356
|
+
| **Unlimited** ⭐ | $9 USDC / month | Scan all you want — _fair use: 50,000/mo, more than any bot needs_ |
|
|
357
|
+
| **Pay as you go** | $0.001 USDC / scan | Only what you use — prepaid or per-call, no commitment |
|
|
358
|
+
|
|
359
|
+
Prepaid keys: send USDC once → `POST /api/buy-key` with the tx signature → use header `X-API-Key` on every scan. Or pay per-call via x402 (`X-Payment-Signature` header). No credit card, no account, no lock-in.
|
|
360
|
+
|
|
361
|
+
**Does it pay for itself?** Work it out with your own numbers rather than ours: the Unlimited tier is $9 a month, so it pays for itself the first time it keeps you out of a position bigger than $9 that goes to zero. Whether that happens once a month or once a week depends on what you trade and how often — we are not going to invent an average for you.
|
|
362
|
+
|
|
363
|
+
Payment is native on Solana — no credit card, no account, no subscription lock-in.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Live dashboard badge
|
|
368
|
+
|
|
369
|
+
Drop a live safety badge into your own bot's dashboard — two lines of HTML, and every token shows its verdict as it trades:
|
|
370
|
+
|
|
371
|
+
```html
|
|
372
|
+
<div class="cabal-hunter-badge" data-mint="YOUR_TOKEN_MINT"></div>
|
|
373
|
+
<script src="https://api.cabal-hunter.com/widget.js" defer></script>
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
It renders the 0–100 score, the plain-English verdict, and the active flags (bundled launch, coordinated dump, whale concentration, serial-launcher deployer, honeypot). Add `data-refresh="120"` to re-scan live as you trade, and `data-api-key="..."` once you're past your free scans. Works anywhere — React, plain HTML, any site.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## API Reference
|
|
75
381
|
|
|
76
|
-
|
|
382
|
+
| Endpoint | Description | Auth |
|
|
383
|
+
|----------|-------------|------|
|
|
384
|
+
| `POST /api/scan-cabal` | Full cabal analysis | $0.001 USDC |
|
|
385
|
+
| `GET /api/scan-cabal?mintAddress=` | GET version | $0.001 USDC |
|
|
386
|
+
| `GET /map?mint=` | Interactive 3D holder map | Free |
|
|
387
|
+
| `GET /api/cex-funding?mint=` | Per-exchange funding breakdown (which CEXes funded holders, % each) | Free |
|
|
388
|
+
| `GET /api/trade-analysis?mint=` | Cohort PnL (Team/Snipers/Insiders) + wash-trading score + exit-liquidity price impact, one call | Free |
|
|
389
|
+
| `POST /api/watch` | Register an emergency dump webhook for a mint (push on dump/rug start) | Free |
|
|
390
|
+
| `GET /api/info` | Pricing, endpoints | Free |
|
|
77
391
|
|
|
78
|
-
|
|
79
|
-
|---|---|---|
|
|
80
|
-
| `CABAL_HUNTER_API_KEY` | *(none)* | Prepaid key for unlimited / metered use (`X-API-Key`). |
|
|
81
|
-
| `CABAL_HUNTER_API` | `https://api.cabal-hunter.com` | Override the API base URL. |
|
|
392
|
+
### Emergency dump webhook (auto-exit)
|
|
82
393
|
|
|
83
|
-
|
|
394
|
+
Instead of polling, let your bot subscribe to a token it holds — we push the moment a coordinated dump or liquidity drain starts:
|
|
84
395
|
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
396
|
+
```bash
|
|
397
|
+
curl -X POST https://api.cabal-hunter.com/api/watch \
|
|
398
|
+
-H "Content-Type: application/json" \
|
|
399
|
+
-d '{"mint":"YOUR_MINT","webhook_url":"https://your-bot.com/dump-alert"}'
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Your endpoint receives:
|
|
403
|
+
```json
|
|
404
|
+
{ "event":"dump_detected", "mint":"...", "reason":"price −34% since last check",
|
|
405
|
+
"coordinated": true, "price_usd": 0.0001, "liquidity_usd": 4200,
|
|
406
|
+
"action":"consider_immediate_exit", "ts": 1781370000 }
|
|
407
|
+
```
|
|
408
|
+
| `GET /health` | Uptime check | Free |
|
|
409
|
+
| `POST /mcp` | MCP tool endpoint | $0.001 USDC per call |
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Infrastructure
|
|
414
|
+
|
|
415
|
+
- **RPC**: Dedicated Helius node (Frankfurt) — fastest Solana data available
|
|
416
|
+
- **Hosting**: AWS EC2 Frankfurt — low latency for EU/global
|
|
417
|
+
- **Analysis**: Real on-chain data — no scrapers, no caches of cached caches
|
|
418
|
+
- **Uptime**: 99.9% target — monitored, auto-restart via systemd
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## FAQ
|
|
423
|
+
|
|
424
|
+
**What is a Solana cabal?**
|
|
425
|
+
A group of wallets — often funded from the same source and buying in the same block — that quietly accumulate a large share of a token's supply before retail, then dump simultaneously into everyone who buys after launch.
|
|
426
|
+
|
|
427
|
+
**How do I check if a Solana token is a rug?**
|
|
428
|
+
Scan the mint with Cabal-Hunter (MCP, REST API, or the free 3D holder map). It traces holder funding back to shared sources, detects same-block bundle buys, flags serial-launcher deployers and live coordinated dumps, and returns an **Exit-Liquidity Risk** verdict: `LOW`, `ELEVATED`, or `HIGH`.
|
|
429
|
+
|
|
430
|
+
**Is it free?**
|
|
431
|
+
Yes — 250 scans/month per IP, with no signup or API key. Beyond that it's $0.001 USDC per scan — which just covers the Helius RPC cost of the live trace — paid natively on Solana.
|
|
432
|
+
|
|
433
|
+
**Can AI trading agents use it?**
|
|
434
|
+
Yes — that's the whole point. The MCP server (`api.cabal-hunter.com/mcp`) lets Claude, Cursor and ElizaOS agents call `check_cabal_risk(mintAddress)` automatically before any swap, and a REST API covers any other language.
|
|
435
|
+
|
|
436
|
+
---
|
|
88
437
|
|
|
89
|
-
##
|
|
438
|
+
## License
|
|
90
439
|
|
|
91
|
-
|
|
440
|
+
MIT — fork it, build on it, integrate it. If you build something with this, share it.
|
|
92
441
|
|
|
93
442
|
---
|
|
94
443
|
|
|
95
|
-
|
|
444
|
+
*Built by [PF Capital](https://api.cabal-hunter.com) · Powered by Helius · Contact: api.cabal-hunter.com/api/info*
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cabal-Hunter API demo — Python
|
|
3
|
+
Checks a Solana token for coordinated wallet clusters before buying.
|
|
4
|
+
|
|
5
|
+
First 250 scans/month are FREE — no signup, no API key, no payment.
|
|
6
|
+
After that: $0.001 USDC per scan via X-Payment-Signature, or a prepaid
|
|
7
|
+
key ($9/month unlimited, fair use 50k) via X-API-Key.
|
|
8
|
+
|
|
9
|
+
Run: python demo/python_demo.py
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import requests
|
|
13
|
+
import json
|
|
14
|
+
|
|
15
|
+
API = "https://api.cabal-hunter.com/api/scan-cabal"
|
|
16
|
+
|
|
17
|
+
# Demo token — known cabal detected
|
|
18
|
+
DEMO_MINT = "Axpzs7FEMYzpcfqVcDjDMQb2rsgMYVJADNpUZe7bpump"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def check_token(mint: str) -> dict:
|
|
22
|
+
"""
|
|
23
|
+
Query Cabal-Hunter for a token.
|
|
24
|
+
Free for the first 250 scans/month. After that, pass X-Payment-Signature
|
|
25
|
+
or X-API-Key.
|
|
26
|
+
"""
|
|
27
|
+
resp = requests.post(API, json={"mintAddress": mint}, timeout=30)
|
|
28
|
+
|
|
29
|
+
if resp.status_code == 200:
|
|
30
|
+
return resp.json()
|
|
31
|
+
|
|
32
|
+
if resp.status_code == 402:
|
|
33
|
+
data = resp.json()
|
|
34
|
+
print("\n💳 Free quota exhausted. Payment required:")
|
|
35
|
+
print(f" Send {data['payment']['amount_usdc']} USDC to {data['payment']['recipient']}")
|
|
36
|
+
print(f" Memo: {data['payment']['memo_required']}")
|
|
37
|
+
print(f" Then re-submit with: X-Payment-Signature: <your_tx_sig>")
|
|
38
|
+
return {}
|
|
39
|
+
|
|
40
|
+
raise RuntimeError(f"Unexpected {resp.status_code}: {resp.text}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def is_safe(report: dict) -> bool:
|
|
44
|
+
"""Returns True if the token passes the cabal check."""
|
|
45
|
+
if not report:
|
|
46
|
+
return False # fail-safe: treat no-response as unsafe
|
|
47
|
+
return not report.get("is_controlled") and report.get("cabal_score", 100) < 35
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
print(f"Checking {DEMO_MINT[:8]}…\n")
|
|
52
|
+
|
|
53
|
+
report = check_token(DEMO_MINT)
|
|
54
|
+
if not report:
|
|
55
|
+
exit(1)
|
|
56
|
+
|
|
57
|
+
free_left = report.get("free_queries_remaining")
|
|
58
|
+
if free_left is not None:
|
|
59
|
+
print(f"✅ Free tier — {free_left} free scans remaining this month\n")
|
|
60
|
+
|
|
61
|
+
print(f"Token: {report.get('token_name', '?')}")
|
|
62
|
+
print(f"Risk: {report.get('risk')}")
|
|
63
|
+
print(f"Cabal Score: {report.get('cabal_score')}/100")
|
|
64
|
+
print(f"Verdict: {report.get('verdict')}")
|
|
65
|
+
print(f"Analysis: {report.get('analysis_time_ms')}ms ({report.get('source')})")
|
|
66
|
+
|
|
67
|
+
clusters = report.get("coordinated_clusters", [])
|
|
68
|
+
if clusters:
|
|
69
|
+
print(f"\n⚠️ {len(clusters)} cluster(s) detected:")
|
|
70
|
+
for c in clusters:
|
|
71
|
+
print(f" {c['wallet_count']} wallets from {c['master_short']} — {c['combined_pct']}% supply [{c['risk']}]")
|
|
72
|
+
|
|
73
|
+
print(f"\n{'🚫 SKIP — cabal detected' if not is_safe(report) else '✅ SAFE — no cabal detected'}")
|
|
74
|
+
print(f"\nVisual map: https://api.cabal-hunter.com/map?mint={DEMO_MINT}")
|
|
Binary file
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cabal-Hunter API demo — TypeScript/Node.js
|
|
3
|
+
* Checks a Solana token for coordinated wallet clusters before buying.
|
|
4
|
+
*
|
|
5
|
+
* First 250 scans/month are FREE — no signup, no payment required.
|
|
6
|
+
* After that: $9/month unlimited, or $0.001 USDC per scan via x402
|
|
7
|
+
* (X-Payment-Signature header).
|
|
8
|
+
*
|
|
9
|
+
* Run: npx tsx demo/typescript_demo.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const API = "https://api.cabal-hunter.com/api/scan-cabal"
|
|
13
|
+
|
|
14
|
+
// Demo token — known cabal detected
|
|
15
|
+
const DEMO_MINT = "Axpzs7FEMYzpcfqVcDjDMQb2rsgMYVJADNpUZe7bpump"
|
|
16
|
+
|
|
17
|
+
interface CabalReport {
|
|
18
|
+
token_name: string
|
|
19
|
+
risk: "HIGH" | "MEDIUM" | "CLEAN"
|
|
20
|
+
cabal_score: number
|
|
21
|
+
is_controlled: boolean
|
|
22
|
+
verdict: string
|
|
23
|
+
coordinated_clusters: Array<{
|
|
24
|
+
master_short: string
|
|
25
|
+
wallet_count: number
|
|
26
|
+
combined_pct: number
|
|
27
|
+
risk: string
|
|
28
|
+
}>
|
|
29
|
+
wallets_checked: number
|
|
30
|
+
analysis_time_ms: number
|
|
31
|
+
source: string
|
|
32
|
+
free_queries_remaining?: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function checkToken(mint: string, paymentSig?: string): Promise<CabalReport | null> {
|
|
36
|
+
const headers: Record<string, string> = { "Content-Type": "application/json" }
|
|
37
|
+
if (paymentSig) headers["X-Payment-Signature"] = paymentSig
|
|
38
|
+
|
|
39
|
+
const resp = await fetch(API, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers,
|
|
42
|
+
body: JSON.stringify({ mintAddress: mint }),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
if (resp.status === 200) return resp.json() as Promise<CabalReport>
|
|
46
|
+
|
|
47
|
+
if (resp.status === 402) {
|
|
48
|
+
const data = await resp.json() as { payment: { amount_usdc: number; recipient: string; memo_required: string } }
|
|
49
|
+
console.log("\n💳 Free quota exhausted. Payment required:")
|
|
50
|
+
console.log(` Send ${data.payment.amount_usdc} USDC to ${data.payment.recipient}`)
|
|
51
|
+
console.log(` Memo: ${data.payment.memo_required}`)
|
|
52
|
+
console.log(` Then re-submit with: X-Payment-Signature: <your_tx_sig>`)
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
throw new Error(`Unexpected ${resp.status}: ${await resp.text()}`)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isSafe(report: CabalReport): boolean {
|
|
60
|
+
return !report.is_controlled && report.cabal_score < 35
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function main() {
|
|
64
|
+
console.log(`Checking ${DEMO_MINT.slice(0, 8)}…\n`)
|
|
65
|
+
|
|
66
|
+
const report = await checkToken(DEMO_MINT)
|
|
67
|
+
if (!report) process.exit(1)
|
|
68
|
+
|
|
69
|
+
if (report.free_queries_remaining !== undefined) {
|
|
70
|
+
console.log(`✅ Free tier — ${report.free_queries_remaining} free scans remaining this month\n`)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(`Token: ${report.token_name}`)
|
|
74
|
+
console.log(`Risk: ${report.risk}`)
|
|
75
|
+
console.log(`Cabal Score: ${report.cabal_score}/100`)
|
|
76
|
+
console.log(`Verdict: ${report.verdict}`)
|
|
77
|
+
console.log(`Analysis: ${report.analysis_time_ms}ms (${report.source})`)
|
|
78
|
+
|
|
79
|
+
if (report.coordinated_clusters.length > 0) {
|
|
80
|
+
console.log(`\n⚠️ ${report.coordinated_clusters.length} cluster(s) detected:`)
|
|
81
|
+
for (const c of report.coordinated_clusters) {
|
|
82
|
+
console.log(` ${c.wallet_count} wallets from ${c.master_short} — ${c.combined_pct}% supply [${c.risk}]`)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`\n${isSafe(report) ? "✅ SAFE — no cabal detected" : "🚫 SKIP — cabal detected"}`)
|
|
87
|
+
console.log(`\nVisual map: https://api.cabal-hunter.com/map?mint=${DEMO_MINT}`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
main().catch(console.error)
|
package/example_bot.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example: Integrate Cabal-Hunter into a Solana trading bot.
|
|
3
|
+
Checks for coordinated wallet clusters before executing any swap.
|
|
4
|
+
Cost: 250 scans/month free, then $0.001 USDC per check (or $9/month
|
|
5
|
+
unlimited). Pays for itself the first time it blocks a rug.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
CABAL_HUNTER_URL = "https://api.cabal-hunter.com/api/scan-cabal"
|
|
11
|
+
MAX_CABAL_SCORE = 35 # block if score >= 35 (controlled token)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_payment_instructions(mint: str) -> dict:
|
|
15
|
+
"""Step 1: Get payment instructions from the API."""
|
|
16
|
+
resp = requests.post(CABAL_HUNTER_URL, json={"mintAddress": mint})
|
|
17
|
+
if resp.status_code == 402:
|
|
18
|
+
return resp.json()["payment"]
|
|
19
|
+
raise ValueError(f"Unexpected response: {resp.status_code}")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def check_cabal(mint: str, payment_tx_sig: str) -> dict:
|
|
23
|
+
"""Step 2: Submit analysis with payment proof."""
|
|
24
|
+
resp = requests.post(
|
|
25
|
+
CABAL_HUNTER_URL,
|
|
26
|
+
json={"mintAddress": mint},
|
|
27
|
+
headers={"X-Payment-Signature": payment_tx_sig},
|
|
28
|
+
)
|
|
29
|
+
if resp.status_code == 200:
|
|
30
|
+
return resp.json()
|
|
31
|
+
raise ValueError(f"Analysis failed: {resp.status_code} {resp.text}")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def is_safe_to_buy(mint: str, payment_tx_sig: str) -> tuple[bool, str]:
|
|
35
|
+
"""
|
|
36
|
+
Returns (safe, reason).
|
|
37
|
+
safe=True means proceed with the trade.
|
|
38
|
+
safe=False means the token has detected cabal risk — skip.
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
result = check_cabal(mint, payment_tx_sig)
|
|
42
|
+
score = result.get("cabal_score", 0)
|
|
43
|
+
risk = result.get("risk", "CLEAN")
|
|
44
|
+
|
|
45
|
+
if result.get("is_controlled") or score >= MAX_CABAL_SCORE:
|
|
46
|
+
clusters = result.get("coordinated_clusters", [])
|
|
47
|
+
c = clusters[0] if clusters else {}
|
|
48
|
+
reason = (
|
|
49
|
+
f"CABAL DETECTED — Score {score}/100 — "
|
|
50
|
+
f"{c.get('wallet_count','?')} coordinated wallets holding "
|
|
51
|
+
f"{c.get('combined_pct','?')}% of supply"
|
|
52
|
+
)
|
|
53
|
+
return False, reason
|
|
54
|
+
|
|
55
|
+
return True, f"CLEAN — Score {score}/100 — {result.get('wallets_checked','?')} wallets traced"
|
|
56
|
+
|
|
57
|
+
except Exception as e:
|
|
58
|
+
# Fail safe: skip trade on any error
|
|
59
|
+
return False, f"Cabal check failed ({e}) — skipping trade as precaution"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ── Example usage ─────────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
token_mint = "PASTE_TOKEN_MINT_HERE"
|
|
66
|
+
|
|
67
|
+
# Step 1: Get payment instructions
|
|
68
|
+
print(f"Checking {token_mint[:8]}…")
|
|
69
|
+
payment = get_payment_instructions(token_mint)
|
|
70
|
+
print(f" → Send {payment['amount_usdc']} USDC to {payment['recipient']}")
|
|
71
|
+
print(f" → Memo: {payment['memo_required']}")
|
|
72
|
+
|
|
73
|
+
# Step 2: You send the USDC (via your wallet SDK), get the tx sig
|
|
74
|
+
payment_sig = input("Paste your transaction signature: ").strip()
|
|
75
|
+
|
|
76
|
+
# Step 3: Check result
|
|
77
|
+
safe, reason = is_safe_to_buy(token_mint, payment_sig)
|
|
78
|
+
print(f"\n{'✅ SAFE' if safe else '🚫 BLOCKED'}: {reason}")
|
|
79
|
+
|
|
80
|
+
if safe:
|
|
81
|
+
print("Proceeding with swap…")
|
|
82
|
+
# execute_swap(token_mint, sol_amount)
|
|
83
|
+
else:
|
|
84
|
+
print("Trade cancelled — protecting your capital.")
|
package/glama.json
ADDED
package/package.json
CHANGED
|
@@ -1,58 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cabal-hunter-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "MCP server for
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "MCP server for Cabal-Hunter \u2014 Solana token rug & cabal detection for AI agents. Wallet clusters, same-block bundles, deployer track record with peak market caps, honeypot and authority traps. 250 free scans/month, no signup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"cabal-hunter-mcp": "index.
|
|
7
|
+
"cabal-hunter-mcp": "server/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"main": "server/index.mjs",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node server/index.mjs"
|
|
8
12
|
},
|
|
9
|
-
"main": "index.js",
|
|
10
|
-
"files": [
|
|
11
|
-
"index.js",
|
|
12
|
-
"README.md",
|
|
13
|
-
"LICENSE"
|
|
14
|
-
],
|
|
15
13
|
"engines": {
|
|
16
14
|
"node": ">=18"
|
|
17
15
|
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"start": "node index.js"
|
|
20
|
-
},
|
|
21
16
|
"dependencies": {
|
|
22
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.7.0"
|
|
23
18
|
},
|
|
24
19
|
"keywords": [
|
|
25
20
|
"mcp",
|
|
26
|
-
"model-context-protocol",
|
|
27
|
-
"mcp-server",
|
|
28
21
|
"solana",
|
|
29
|
-
"rug-check",
|
|
30
22
|
"rug-detection",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"pump-fun",
|
|
35
|
-
"pumpfun",
|
|
36
|
-
"token-safety",
|
|
37
|
-
"honeypot-detection",
|
|
38
|
-
"exit-liquidity",
|
|
39
|
-
"trading-bot",
|
|
40
|
-
"ai-agent",
|
|
41
|
-
"ai-agents",
|
|
42
|
-
"web3",
|
|
43
|
-
"defi",
|
|
44
|
-
"claude",
|
|
45
|
-
"cursor",
|
|
46
|
-
"elizaos"
|
|
23
|
+
"cabal",
|
|
24
|
+
"trading",
|
|
25
|
+
"model-context-protocol"
|
|
47
26
|
],
|
|
48
27
|
"license": "MIT",
|
|
49
|
-
"author": "Cabal-Hunter (https://api.cabal-hunter.com)",
|
|
50
|
-
"homepage": "https://api.cabal-hunter.com",
|
|
51
28
|
"repository": {
|
|
52
29
|
"type": "git",
|
|
53
|
-
"url": "git+https://github.com/paulf280-ui/
|
|
30
|
+
"url": "git+https://github.com/paulf280-ui/solana-safe-sniper-mcp-template.git"
|
|
54
31
|
},
|
|
55
|
-
"
|
|
56
|
-
"url": "https://github.com/paulf280-ui/cabal-hunter-mcp/issues"
|
|
57
|
-
}
|
|
32
|
+
"homepage": "https://api.cabal-hunter.com"
|
|
58
33
|
}
|
package/server/index.mjs
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cabal-Hunter — local stdio MCP server.
|
|
4
|
+
*
|
|
5
|
+
* A thin Model Context Protocol server that exposes one tool, `check_cabal_risk`,
|
|
6
|
+
* and proxies to the hosted Cabal-Hunter API (https://api.cabal-hunter.com).
|
|
7
|
+
* Drop it into any MCP client (Claude Desktop, Cursor, ElizaOS) to give an agent
|
|
8
|
+
* a pre-trade safety check that catches coordinated wallet cabals, same-block
|
|
9
|
+
* Jito bundles, the dev's launch track record (peak market cap per past token +
|
|
10
|
+
* pump-and-dump history) and live coordinated dumps on any Solana mint before it
|
|
11
|
+
* signs a swap.
|
|
12
|
+
*
|
|
13
|
+
* Free tier: 250 scans/month per IP, no signup. Beyond that it is $9/month
|
|
14
|
+
* unlimited (fair use), or $0.001 USDC per scan via x402 on Solana — when a
|
|
15
|
+
* scan needs payment the API returns HTTP 402 with the payment instructions,
|
|
16
|
+
* which this tool surfaces to the caller.
|
|
17
|
+
*
|
|
18
|
+
* Uses the low-level Server API + JSON-Schema tool definitions for maximum
|
|
19
|
+
* compatibility across @modelcontextprotocol/sdk versions.
|
|
20
|
+
*/
|
|
21
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
22
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
23
|
+
import {
|
|
24
|
+
ListToolsRequestSchema,
|
|
25
|
+
CallToolRequestSchema,
|
|
26
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
27
|
+
|
|
28
|
+
const API_BASE = process.env.CABAL_HUNTER_API || "https://api.cabal-hunter.com";
|
|
29
|
+
const SCAN_URL = `${API_BASE}/api/scan-cabal`;
|
|
30
|
+
|
|
31
|
+
const server = new Server(
|
|
32
|
+
{ name: "cabal-hunter", version: "1.0.0" },
|
|
33
|
+
{ capabilities: { tools: {} } }
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const CHECK_CABAL_RISK = {
|
|
37
|
+
name: "check_cabal_risk",
|
|
38
|
+
description:
|
|
39
|
+
"Scan a Solana token mint for coordinated wallet cabals, same-block Jito " +
|
|
40
|
+
"bundle buys, the dev's launch track record (peak market cap per past token " +
|
|
41
|
+
"+ pump-and-dump history) and live coordinated dumps. Returns an " +
|
|
42
|
+
"Exit-Liquidity Risk verdict (cabal_score 0-100, risk level, is_controlled, " +
|
|
43
|
+
"coordinated wallet clusters) so a trading agent can decide whether it is " +
|
|
44
|
+
"about to become exit liquidity BEFORE it signs a swap. Works on pump.fun, " +
|
|
45
|
+
"PumpSwap and Raydium tokens.",
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties: {
|
|
49
|
+
mintAddress: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "The Solana token mint address (base58) to scan.",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ["mintAddress"],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
59
|
+
tools: [CHECK_CABAL_RISK],
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
63
|
+
if (request.params.name !== "check_cabal_risk") {
|
|
64
|
+
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
65
|
+
}
|
|
66
|
+
const mintAddress = request.params.arguments?.mintAddress;
|
|
67
|
+
if (!mintAddress || typeof mintAddress !== "string") {
|
|
68
|
+
return {
|
|
69
|
+
isError: true,
|
|
70
|
+
content: [{ type: "text", text: "mintAddress is required (base58 mint string)." }],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const resp = await fetch(SCAN_URL, {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: { "Content-Type": "application/json" },
|
|
78
|
+
body: JSON.stringify({ mintAddress }),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// x402: payment required — surface the instructions rather than failing.
|
|
82
|
+
if (resp.status === 402) {
|
|
83
|
+
const body = await resp.json().catch(() => ({}));
|
|
84
|
+
return {
|
|
85
|
+
content: [
|
|
86
|
+
{
|
|
87
|
+
type: "text",
|
|
88
|
+
text:
|
|
89
|
+
"Payment required (x402). Free tier exhausted — this scan costs " +
|
|
90
|
+
"$0.02 USDC on Solana.\n\n" +
|
|
91
|
+
JSON.stringify(body.payment || body, null, 2),
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!resp.ok) {
|
|
98
|
+
const text = await resp.text().catch(() => "");
|
|
99
|
+
return {
|
|
100
|
+
isError: true,
|
|
101
|
+
content: [{ type: "text", text: `Scan failed: HTTP ${resp.status} ${text}` }],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const data = await resp.json();
|
|
106
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return {
|
|
109
|
+
isError: true,
|
|
110
|
+
content: [{ type: "text", text: `Scan error: ${err?.message || String(err)}` }],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
async function main() {
|
|
116
|
+
const transport = new StdioServerTransport();
|
|
117
|
+
await server.connect(transport);
|
|
118
|
+
// stdio transport keeps the process alive; log to stderr so stdout stays clean.
|
|
119
|
+
console.error("cabal-hunter MCP server running on stdio");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
main().catch((err) => {
|
|
123
|
+
console.error("fatal:", err);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|
package/index.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* cabal-hunter-mcp — local MCP server for on-chain Solana cabal/rug detection.
|
|
4
|
-
*
|
|
5
|
-
* Exposes one tool, `check_cabal_risk`, that scans any Solana token mint and
|
|
6
|
-
* returns an exit-liquidity risk verdict (SAFE / REVIEW / AVOID), a 0-100 cabal
|
|
7
|
-
* score, funding-cluster + same-block-bundle + coordinated-dump detection,
|
|
8
|
-
* serial-rug deployer history, and a honeypot (freeze / Token-2022) check.
|
|
9
|
-
*
|
|
10
|
-
* It is a thin stdio wrapper over the hosted Cabal-Hunter API
|
|
11
|
-
* (https://api.cabal-hunter.com) so any MCP client — Claude Desktop, Claude
|
|
12
|
-
* Code, Cursor, VS Code, ElizaOS — can run it with `npx cabal-hunter-mcp`.
|
|
13
|
-
*
|
|
14
|
-
* Free tier: 250 scans/month per IP, no signup, no key. To use a prepaid key
|
|
15
|
-
* (e.g. $9/mo unlimited), set CABAL_HUNTER_API_KEY.
|
|
16
|
-
*/
|
|
17
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
18
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
|
-
import {
|
|
20
|
-
ListToolsRequestSchema,
|
|
21
|
-
CallToolRequestSchema,
|
|
22
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
23
|
-
|
|
24
|
-
const API = (process.env.CABAL_HUNTER_API || "https://api.cabal-hunter.com").replace(/\/+$/, "");
|
|
25
|
-
const API_KEY = process.env.CABAL_HUNTER_API_KEY || "";
|
|
26
|
-
|
|
27
|
-
const TOOL = {
|
|
28
|
-
name: "check_cabal_risk",
|
|
29
|
-
description:
|
|
30
|
-
"Real-time on-chain coordinated-wallet (cabal) and rug detection for any " +
|
|
31
|
-
"Solana token mint. One call returns an Exit-Liquidity Risk verdict " +
|
|
32
|
-
"(SAFE | REVIEW | AVOID), a 0-100 cabal score, funding-cluster detection " +
|
|
33
|
-
"(top holders funded by the same source), same-block Jito-bundle detection, " +
|
|
34
|
-
"coordinated-dump detection, serial-rug deployer history (e.g. 'launched 14, " +
|
|
35
|
-
"13 dead'), a honeypot check (freeze authority + Token-2022 traps), and " +
|
|
36
|
-
"on-chain evidence transactions for every flag. Use it before an agent buys a " +
|
|
37
|
-
"pump.fun / PumpSwap / Raydium token to answer: are you the exit liquidity? " +
|
|
38
|
-
"Free: 250 scans/month, no API key required.",
|
|
39
|
-
inputSchema: {
|
|
40
|
-
type: "object",
|
|
41
|
-
properties: {
|
|
42
|
-
mint: {
|
|
43
|
-
type: "string",
|
|
44
|
-
description:
|
|
45
|
-
"The Solana token mint (contract) address to scan, e.g. " +
|
|
46
|
-
"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
required: ["mint"],
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const server = new Server(
|
|
54
|
-
{ name: "cabal-hunter", version: "0.1.0" },
|
|
55
|
-
{ capabilities: { tools: {} } }
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [TOOL] }));
|
|
59
|
-
|
|
60
|
-
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
61
|
-
const { name, arguments: args } = req.params;
|
|
62
|
-
if (name !== TOOL.name) {
|
|
63
|
-
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
64
|
-
}
|
|
65
|
-
const mint = String(args?.mint || "").trim();
|
|
66
|
-
if (!mint) {
|
|
67
|
-
return {
|
|
68
|
-
content: [{ type: "text", text: "Error: 'mint' (a Solana token mint address) is required." }],
|
|
69
|
-
isError: true,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
const url = `${API}/api/scan-cabal?mintAddress=${encodeURIComponent(mint)}`;
|
|
73
|
-
const headers = { Accept: "application/json" };
|
|
74
|
-
if (API_KEY) headers["X-API-Key"] = API_KEY;
|
|
75
|
-
try {
|
|
76
|
-
const res = await fetch(url, { headers });
|
|
77
|
-
const text = await res.text();
|
|
78
|
-
if (!res.ok) {
|
|
79
|
-
return {
|
|
80
|
-
content: [{ type: "text", text: `Cabal-Hunter API error ${res.status}: ${text.slice(0, 800)}` }],
|
|
81
|
-
isError: true,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return { content: [{ type: "text", text }] };
|
|
85
|
-
} catch (e) {
|
|
86
|
-
return {
|
|
87
|
-
content: [{ type: "text", text: `Request to Cabal-Hunter failed: ${e?.message || String(e)}` }],
|
|
88
|
-
isError: true,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const transport = new StdioServerTransport();
|
|
94
|
-
await server.connect(transport);
|
|
95
|
-
console.error(`cabal-hunter MCP server running on stdio → ${API}`);
|