scamcheck
Runs scamcheck as a Telegram bot, allowing users to submit suspicious messages for local scam triage and receive verdicts, signals, and intelligence briefings.
Runs scamcheck as a WhatsApp bot, enabling message triage and scam-analysis responses through WhatsApp using a configured token and phone ID.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@scamcheckcheck this message for scam signals: Your KYC expires today, verify at bit.ly/x1y2z"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
scamcheck
Offline-first scam triage and evidence kit.
One message in → an explainable verdict, the exact signals that fired, the scam playbook, what the attacker will try next, and a tamper-evident case file out.
No API keys. No dependencies. No telemetry. Python 3.9+ and nothing else.
Most scam tools ask you to upload the victim's messages to someone else's server. scamcheck runs the analysis where the message already is — on your machine — and keeps the evidence there too. That makes it usable on material you would never paste into a website: a family member's SMS, a leaked chat export, a client's complaint file.
message ──▶ normalize ──▶ signals + playbook ──▶ verdict (explained)
│
├──▶ intelligence: type, stage, next move, verify
├──▶ indicators: phones, UPI, wallets, domains
└──▶ case: SHA-256 custody, timeline, exportWhy it is different
Deterministic and explainable. Every verdict lists the signals that produced it, with weights. Same input → same output, every time. No model drift, no API dependency, no "trust me". You can diff two versions of the engine and see exactly what changed.
Offline by default. Analysis never opens a socket. The only networked actions are explicit and opt-in:
--update-feeds,url <link>,check_urlover MCP, the chat bots, and an AI key you set yourself.Evidence-grade. Cases hash every artefact at collection time (SHA-256), record a timeline, extract entities, and export a pack with a
SHA256SUMSfile.--case-verifydetects later tampering. Very few open-source detection tools go past "here is a score".
Related MCP server: AssemblyLine MCP
Install
git clone https://github.com/Ctrlanmol/scamcheck.git
cd scamcheck
python3 -m pip install -e .
scamcheck --versionThere is nothing else to install — the Python core is standard library only.
If you would rather not install anything, every command below also works as
python3 -m scamcheck ... from the repo root.
Demo
$ scamcheck "Dear customer, your KYC is expiring today. Verify now at http://bit.ly/x1y2z or your account will be blocked within 24 hours."
🚨 VERDICT: SCAM
Rules score: ██████████████░░░░░░░░ 63/100
Intel: This matches bank transfer fraud (current stage: building trust / making contact). Likely goal: get you to send money.
Stage: building trust / making contact
Likely next: They will move the chat to WhatsApp or Telegram where moderation is weaker.
Why (4 signals):
• Shortened URL hides the real destination: bit.ly (severity 25 [+25 points])
• Threatens account suspension (severity 18.0 [+18.0 points])
• KYC urgency trick (common bank-scam opener) (severity 12.0 [+12.0 points])
• Manufactured urgency (severity 8.0 [+8.0 points])--intel adds the full briefing: scam type and confidence, kill-chain stage,
attacker goals, payment rail, impersonated brand, five likely next moves,
independent verification steps, safe replies, and what evidence to preserve.
$ scamcheck --intel "..." | head -20
🧠 Intelligence briefing
This matches bank transfer fraud (current stage: building trust / making contact). Likely goal: get you to send money.
Type: bank transfer fraud (confidence: high)
Stage: building trust / making contact
Goal: Get you to send money; Take over or freeze your account; Collect your identity documents
Payment: Bank account / transfer
Channel: SMS / chat (format not stated)
Tactics: Artificial urgency or a countdown; Threats: block, freeze, fine, arrest or loss; False familiarity or trust-building
Likely next moves:
• They will move the chat to WhatsApp or Telegram where moderation is weaker.
• They will build rapport before mentioning money at all.
• A 'bank officer' may call and ask for your OTP/PIN or a transfer to a 'safe account'.
• They may send a fake case ID or freeze notice with a tight deadline.
• They may ask you to install a remote-access or 'bank update' app.Use it from an AI agent (MCP)
scamcheck ships a stdio MCP server, so any agent that speaks Model Context Protocol can triage a message without shipping it to a cloud model.
scamcheck --mcp{
"mcpServers": {
"scamcheck": { "command": "scamcheck", "args": ["--mcp"] }
}
}Tool | Network | What it returns |
| no | verdict, score, signals, playbook, full intelligence briefing |
| yes | redirect chain, TLS, RDAP age, punycode/raw-IP flags |
| no | E.164, country, line type, premium ranges, local reputation |
| no | phones, UPI IDs, wallets, domains, emails, handles, APK links |
| no | a local case with SHA-256 hashed evidence |
| no | version, capabilities, data directory |
check_url is the only tool that touches the network, and it refuses private,
loopback and link-local targets. Everything else is deterministic and local.
Cheat sheet
# one message
scamcheck "Your account will be blocked in 24 hours. Verify now: bit.ly/x1y2z"
scamcheck --intel "..." # full intelligence briefing
# a whole conversation (pig butchering / task scams)
scamcheck --chat chat_transcript.txt
# files, clipboard, screenshots
scamcheck -f message.txt
pbpaste | scamcheck --stdin
scamcheck --image ~/Desktop/screenshot.png # OCR + QR decode
# senders and numbers (local lists + cached feeds)
scamcheck "Hi, is this the right number?" --sender "+91 98765 43210"
scamcheck --number "+1 900 555 1234"
scamcheck --block "+91 98765 43210" --label "KYC scam"
scamcheck --allow "+91 98765 43210" --label "my bank"
# watch everything you copy, or a screenshot folder
scamcheck --watch
scamcheck --watch --screenshot-dir ~/Desktop
# machine-readable
scamcheck "..." --json
scamcheck --batch inbox.txt --json # blank-line separated messages
# local REST API + family web UI
scamcheck --serve # http://127.0.0.1:8765
# chat bots
scamcheck --bot telegram # needs SCAMCHECK_TELEGRAM_TOKEN
scamcheck --bot whatsapp # needs WHATSAPP_TOKEN + phone id
scamcheck --bot-status
# JARVIS console: text, screenshots, QR, links, numbers, memory
scamcheck --jarvis --voice --notify
# evidence cases + complaint draft (all local)
scamcheck --case-new "UPI fraud" --scam-type upi --loss 5000 --currency INR
scamcheck --case-add CASE_ID screenshot.png
scamcheck --case-verify CASE_ID
scamcheck --case-export CASE_ID
scamcheck --report CASE_ID --country IN --report-out complaint.txt
# watchlists + campaign clustering
scamcheck --watch-add "upi:scammer@okhdfcbank"
scamcheck --watch-add "url:evil-example.xyz"
scamcheck --watchlist
scamcheck --campaigns
# recovery checklist for a scam type and country
scamcheck --recover upi --country IN
# read-only device + private-LAN audit (no sudo)
scamcheck --device-audit --device-full
scamcheck --device-audit --device-active # TCP probes, own LAN only
# AI agent server
scamcheck --mcpPrefer the browser? cd web && npm install && npm run dev starts the full
Next.js interface and documentation site at http://127.0.0.1:3000.
To run the test suite:
python3 tests/run_samples.py
python3 tests/test_batch.py
python3 tests/test_intelligence.py
python3 tests/test_phones.py
python3 tests/test_cases.py
python3 tests/test_webcli.py
python3 tests/test_mcp.py
# macOS only
python3 tests/test_ocr.py
python3 tests/test_deviceaudit.pyWho it is for
You are | You get |
A developer |
|
A security analyst | IOC extraction, URL metadata (redirects, TLS, RDAP), case packs with hash custody you can attach to a ticket. |
Trust & safety / fraud ops |
|
An OSINT researcher, journalist or NGO | Analysis of victim material that never leaves the machine. Works air-gapped. |
A privacy-minded individual | A clipboard watcher and a family web UI with no account, no cloud, no telemetry. |
Someone looking after a parent |
|
An AI agent builder |
|
What it is not
Read this before trusting a verdict.
Not a threat-intel network. Reputation comes from your local lists and feeds you configure. There is no telemetry, so it cannot know that a number was reported by ten thousand other people.
Not a browser extension or a mobile app. It runs where Python runs. A thin browser client and an Android build are on the roadmap.
Not a malware sandbox.
urlreads public HTTP(S) metadata. Never open a suspect file because a tool said it was "probably fine".Not encrypted at rest. Case vaults and databases are
0600/0700plaintext. Use FileVault or LUKS for sensitive cases; encrypted vaults are planned.Not authenticated.
--serve --host 0.0.0.0is unauthenticated by design (it is meant for your own LAN). Do not expose it to an untrusted network.Not a replacement for a human decision. A verdict is evidence to reason with, not proof of a crime.
Docs
File | Contents |
Market research, competitive positioning, launch plan, 90-day roadmap | |
Shipped history and planned work, version by version | |
Scam taxonomy and 2025–26 research base | |
Guarantees, limits, safe evidence handling | |
How to add a playbook, a language or a specimen |
Contributing
The most useful contributions are small: a new playbook with two mandatory
beats, a new language's signals, a redacted specimen that breaks the engine, or
a translation of the family UI. Start with
CONTRIBUTING.md and the good first issue label.
Please do not open a public issue for a detection bypass that would help a
scammer evade the tool — see SECURITY.md.
License
MIT. Use it, fork it, ship it inside your bank if you like. Attribution is appreciated, not required.
How it works
Four layers:
Rule engine (always on, instant, offline) — ~45 weighted signals covering:
urgency & final-warning pressure ("act now", "suspended in 24 hours")
banking / OTP / KYC phishing scripts ("verify your account", "do not share this code")
lottery, prize, tax-refund, courier-fee and fake-job rackets
payment tricks (gift cards, "small processing fee", crypto, wire transfers)
link forensics: shortened URLs, raw IP links, punycode/homoglyph domains, lookalike brands (
paypa1-secure.xyz), risky TLDs (.xyz,.top,.ru, …)impersonation of banks & couriers, remote-access app installs (TeamViewer/AnyDesk)
These combine into a score 0–100 and a verdict:
Score
Verdict
Exit code
0–14
LIKELY SAFE
0
15–34
SUSPICIOUS
1
35–59
LIKELY SCAM
1
60–100
SCAM
2
Intelligence layer (always on, deterministic) — maps the signals to a scam type, kill-chain stage, attacker goals, payment rails, impersonation, likely next moves and a verification/evidence plan. It also repairs common obfuscation (zero-width characters, homoglyphs, spacing, disguised URLs) for a secondary matching pass. See
--intelbelow.AI second opinion (optional) — when a message lands in the borderline band (or you pass
--ai always), it's sent to an LLM with the rule findings as a hint. High-confidence AI answers can escalate or soften the verdict.Phone-number evidence (optional) — a sender number adds range facts (premium-rate, shortcode, foreign country vs local bank cues) and local reputation hits. It can raise a verdict, never lower the text verdict, and is read entirely from disk.
Intelligence briefing (--intel)
A verdict tells you whether a message is risky. The intelligence layer tells
you what kind of operation it is, where it is in the playbook, and what to do
next. It is deterministic, local and explainable — no LLM and no network — and
it is attached to every --json result as intelligence.
python3 -m scamcheck --intel "Hi dear, guaranteed returns on our platform. Deposit 500 USDT. Pay 20% tax to withdraw."The briefing includes:
Scam type and confidence — bank/OTP, UPI, card, crypto, investment/job, romance, sextortion, tech support, digital arrest, courier, loan, identity
Kill-chain stage — trust → money pivot → platform → deposit → withdrawal fee → pressure → isolation (whole-chat transcripts use the trajectory engine)
Attacker goals — money transfer, crypto deposit, credentials/OTP, remote access, identity documents, account takeover, blackmail, advance fee
Payment rails — UPI ID, bank/IFSC, card, crypto address, gift card or payment app, with the exact value found in the message
Impersonation — named banks, authorities, delivery firms and payment apps
Likely next moves — the specific escalation this script usually takes
Verification steps — how to check independently, using a channel you find yourself rather than the one supplied by the sender
Safe replies — neutral lines if you must respond (with a clear "no reply is best" policy on high-risk messages)
Evidence guidance — what to screenshot, export and preserve before blocking
Obfuscation report — zero-width characters, Cyrillic/Greek lookalikes, full-width letters, letter-spacing, leetspeak and disguised URLs (
hxxp,[.],(dot))
In JARVIS, assess <text> shows the full briefing; next, reply and brief
explain the last check without re-sending the message.
Enabling the AI layer
Any one of these is enough, then it activates automatically:
export OPENAI_API_KEY="sk-..." # OpenAI, or any OpenAI-compatible service
# optional for alternatives / self-hosting:
export OPENAI_BASE_URL="https://api.your-proxy.com/v1"
export SCAMCHECK_AI_MODEL="gpt-4o-mini"
export ANTHROPIC_API_KEY="sk-ant-..." # or AnthropicNo key set, network down, or bad response? The tool silently keeps going with rules only — it never blocks on AI.
For --image OCR, the AI-vision fallback tier uses the same key
(SCAMCHECK_VISION_MODEL, default gpt-4o-mini) but is only tried when both
local engines are unavailable or produce nothing.
Screenshot checking (--image, --watch)
The OCR fallback chain — first available wins:
Apple Vision (macOS 10.15+): built in, zero installs
Tesseract: if
tesseractis on PATH (brew install tesseract)AI vision: only if an API key is set and both local engines failed
Noisy OCR (OCR-mangled glyphs like 5U5PENDED, RCCOUNT) is matched against
the signals through a separate normalized pass at half weight, so screenshots
still scan even on ugly fonts. QR/barcodes are decoded with Apple Vision too:
a QR-only screenshot works, and the payload is fed through the normal link
analysis (offline — decoding does not open the link). Clipboard HTML/images are
read via AppleScript-ObjC.
Languages: Hindi / Hinglish / Devanagari
India is the biggest victim market and most of that bait arrives in Hinglish ("aapka account band ho jayega", "rozana kamai") or Devanagari. v0.4 ships 24 targeted signals, all playbook fingerprints, and the full conversation-trajectory detector in both scripts — a Hinglish part-time task-scam WhatsApp transcript scores SCAM end to end.
One OCR caveat: Apple Vision on current macOS lists Hindi as supported but
ships no usable Devanagari model (verified — Devanagari test images OCR as
garbage). For Hindi screenshots either install tesseract's Hindi pack
(brew install tesseract-lang, picked up automatically) or enable the AI
vision tier; English/Hinglish-in-Roman OCR is unaffected.
Batch checking (--batch)
Checking a whole inbox export or a pile of forwarded messages:
scamcheck --batch messages.txt # blocks separated by blank lines
cat messages.txt | scamcheck --batch - # or from stdin
scamcheck --batch messages.txt --json # machine-readable array
scamcheck --batch chats.txt --chat # each block is a full transcriptEach blank-line-separated block is one message by default (SMS exports wrap
lines, and conversation mode would suppress per-message rules). --chat
switches to trajectory analysis per block. Exit code is 2 if any message is
SCAM/LIKELY SCAM, 1 if any is SUSPICIOUS, else 0 — so it slots into scripts.
Tuning
Everything lives in scamcheck/rules.py:
SIGNAL_RES— add/remove(regex, weight, label)lines for scam patterns you meet locally (weights are points added to the score)_OCR_VOCAB— the word list used for fuzzy repair of OCR-noisy screenshotsSHORTENERS,RISKY_TLDS,BRANDS— extend the link forensics listsverdict thresholds live at the bottom of
run_rules()
Phone-number intelligence
Attach the sender (--sender) and scamcheck folds number evidence into the
message verdict; --number checks one number on its own:
python3 -m scamcheck "Hi, is this the right number?" --sender "+91 98765 43210"
python3 -m scamcheck --number "+1 900 555 1234" --jsonIt understands:
premium-rate numbers — IN
1900, US/CA900, UK09, AU190x: calling or texting them costs extraSMS shortcodes and alphanumeric sender IDs (
VM-HDFCBK) — not personal lines, and sender IDs are spoofablecountry mismatch — e.g. a
+1personal number claiming to be HDFC while the message talks in ₹/UPI/KYC termsyour blocklist / allowlist and cached feed reports
Everything lives under ~/.scamcheck/ (override with SCAMCHECK_HOME):
File | Purpose |
| numbers you marked as scams (one per line, optional |
| numbers you trust — wins over the blocklist |
| cache written only by an explicit |
| the public list URLs you choose |
| default region |
| JARVIS local memory (SQLite, |
| evidence-case metadata, entities, evidence hashes and timeline ( |
| private evidence vault: one folder per case ( |
| default case-pack exports (JSON/MD/HTML/SHA256SUMS, optional PDF) |
| local watchlist identifiers + hit counters ( |
| voice/notify/language/sender preferences |
scamcheck --block "+91 98765 43210" --label "KYC scam"
scamcheck --allow "+91 98765 43210" --label "my bank"
scamcheck --forget "+91 98765 43210"
scamcheck --show-lists
scamcheck --import-list spam.csv # TXT or CSV (blocklist)
scamcheck --import-list trusted.csv --import-allow
scamcheck --set-region US # national-number interpretation
scamcheck --help # all flagsFeeds (opt-in, never automatic)
There is no bundled blocklist, on purpose. Free, trustworthy, bulk phone-number feeds barely exist: OpenPhish/PhishTank are URL lists, Truecaller has no public API, and regulator data is unverified complaint reports. So scamcheck is feed-agnostic and starts empty:
# ~/.scamcheck/feeds.conf
# URL | label | format (txt, csv, csv:number,label,reports)
https://example.org/spam-numbers.csv | Community list | csvscamcheck --update-feeds # the ONLY command that touches the network
scamcheck --feed-statusHTTPS only (plain HTTP allowed only for
127.0.0.1self-hosting)25 MB / 200k entries per feed; a failed refresh keeps the previous cache
only the configured list URLs are fetched — message text is never uploaded
no automatic refresh: analysis reads the cache and nothing else
One real-world option is the US FTC's daily Do-Not-Call complaint CSVs
(unverified consumer reports, not proof; US numbers only, ~1 MB/day) — usable
through feeds.conf, but deliberately not enabled for you.
The default region is IN. Change it with --set-region US or
SCAMCHECK_REGION=GB; national numbers are interpreted in that region.
Family web UI
python3 -m scamcheck --serve also serves a one-page UI at
http://127.0.0.1:8765/ — built for the "check this for me" moment with a
teen or an elder:
one big paste box + one big button, 19px text, high contrast, keyboard and screen-reader friendly (Ctrl/⌘+Enter also checks)
screenshots: choose a file, drag-drop, or just paste a screenshot anywhere on the page — OCR runs locally, extracted text is shown
verdicts in plain language with what to do next, not just a score
optional sender number field — shows "sender is on your blocklist" and premium-rate warnings inline
English / हिंदी toggle (choice is remembered)
no external fonts, scripts, or analytics; strict CSP (
default-src 'none'); page and API are same-origin and localhost-only by default
To let another device on your Wi-Fi use it:
python3 -m scamcheck --serve 8765 --host 0.0.0.0That exposes the API to your network with no authentication — only do it on a network you trust, and stop the server when you're done.
Next.js interface (web/)
web/ is the full local interface: an editorial, documentation-backed app that
exposes every scamcheck tool — message and screenshot checks, numbers and
feeds, JARVIS, evidence cases, watchlist, campaigns, recovery, device audit and
settings. It is a shell over the Python core, not a reimplementation: the
browser calls a JSON bridge (scamcheck/webcli.py) which imports the same
modules as the CLI, so verdicts, hashes and audit results are identical in both
places.
cd web
npm install
npm run dev # http://127.0.0.1:3000Production build:
cd web
npm run build
npm startRequirements: Node.js 20+ for the interface. The Python core still needs only Python 3.9+ and the standard library — no Python packages are installed.
Pages
Page | Path | What it does |
Overview |
| Runtime status, recent checks, quick check |
Message check |
| Message, conversation, screenshot, batch, link and QR |
Numbers & feeds |
| Number intel, local block/allow lists, imports, feeds |
JARVIS |
| The terminal command grammar in a chat console |
Evidence cases |
| Create cases, add hashed evidence, verify, export, report |
Watchlist |
| Watch phones, domains, UPI IDs, wallets and handles |
Campaigns |
| Related checks clustered by shared indicators |
Recovery |
| Country-aware playbooks for 15 scam types |
Device audit |
| Read-only machine + private-LAN audit, streamed live |
Settings |
| Region, local memory, bot status, data files |
Documentation |
| Overview, install, CLI, web, API, device, cases, privacy, FAQ |
How the bridge works
POST /api/tooltakes{"tool": "...", "args": {...}}, validates the tool against a whitelist, spawnspython3 -m scamcheck.webcliin the project root and returns{"ok": true, "data": ...}or an error.POST /api/device-auditstreams progress over Server-Sent Events, then a singleresultevent with the report and export paths.The bridge finds the project root by walking up from
web/looking forscamcheck/__init__.pyandpyproject.toml. Override it withSCAMCHECK_ROOTif the app runs from elsewhere.
Environment variables
Variable | Used by | Purpose |
| Core + web | Data directory (default |
| Core + web | Default region, e.g. |
| Web | Project root containing |
| Web | Python binary to spawn (default |
The web app has no authentication and no database of its own. Keep it on
loopback; do not expose it to a LAN or the internet. It does not touch an
existing service on port 8765 — the built-in API and family UI stay separate.
Full documentation lives in the app at /docs (install, CLI reference, web
interface, HTTP API, device audit, cases and recovery, privacy model, FAQ).
Chat bots (Telegram / WhatsApp)
The detector, in chat: send a message or a screenshot and get a plain-language verdict. The analysis runs on the machine hosting the bot; the only outbound traffic is the reply itself (and, for WhatsApp, normal Cloud API calls).
Telegram — long polling, no public URL
Create a bot with @BotFather and copy the token.
Run:
export SCAMCHECK_TELEGRAM_TOKEN="123456:ABC..."
python3 -m scamcheck --bot telegram
# or: scamcheck --bot telegram --bot-token "..."Send text, a photo/screenshot, or an image file. Commands:
Command | Effect |
| reply language (per chat, remembered) |
| remember a sender number for this chat |
| forget it |
| check one message explicitly |
| region, language, sender, cached feed count |
| usage |
Limits and privacy:
20 checks/hour/chat by default (
SCAMCHECK_BOT_RATE), so a group can't spam ittext/screenshots are analyzed locally; nothing is sent to an AI unless you set
SCAMCHECK_BOT_AI=auto|alwaysand an API keyTelegram doesn't expose the original sender number when you forward a message, so set
/sendermanually when that mattersthe bot process must stay running; long polling needs no ports or certificates
WhatsApp — Cloud API + public HTTPS webhook
Create a Meta app with the WhatsApp product and a test number. Collect
WHATSAPP_TOKENandWHATSAPP_PHONE_NUMBER_ID.Start the webhook locally:
export WHATSAPP_TOKEN="EAAG..."
export WHATSAPP_PHONE_NUMBER_ID="1234567890"
export WHATSAPP_VERIFY_TOKEN="choose-a-random-string"
export WHATSAPP_APP_SECRET="..." # optional, recommended (HMAC checks)
python3 -m scamcheck --bot whatsapp # 127.0.0.1:8766/webhook/whatsappGive Meta a public HTTPS URL with a tunnel:
cloudflared tunnel --url http://127.0.0.1:8766In Meta → WhatsApp → Configuration, set the callback URL to
https://<your-tunnel-host>/webhook/whatsappand the verify token to the same value. Subscribe tomessages.
Notes:
the webhook acknowledges Meta immediately and processes the message in the background, so retries don't double-reply (message IDs are deduplicated)
images are downloaded through the Graph API, OCR'd locally, and answered
WHATSAPP_APP_SECRETenablesX-Hub-Signature-256verification on every POSTbind defaults to
127.0.0.1; put it behind a tunnel/reverse proxy rather than exposing the port. There is no user authentication on the webhook itself.
python3 -m scamcheck --bot-status prints what is configured without making any
network calls (tokens are masked).
JARVIS console (--jarvis)
One local command center for everything the detector can do, with memory and optional voice/notifications:
scamcheck --jarvis # interactive console
scamcheck --jarvis --voice --notify # spoken + desktop alerts for risky results
printf 'check URGENT: verify at bit.ly/x\nquit\n' | scamcheck --jarvisCommand | What it does |
| analyze an SMS/email/chat message |
| full intelligence brief: type, stage, goals, next moves, verification, evidence |
| briefing, attacker next moves, or safe replies for the last check |
| OCR a screenshot, decode QR codes, then analyze |
| decode QR/barcodes only; never opens them |
| check the current clipboard (text or screenshot) |
| redirect chain, TLS, domain age, final page title |
| range facts, premium-rate, local lists, cached feeds |
| edit your local number lists |
| attach a sender number to subsequent checks |
| local memory |
|
|
| local watchlist |
| cluster related checks by shared indicators |
| country-aware recovery playbook |
| draft a complaint from a case |
| read-only device audit (posture, processes, persistence, browsers, network, files) |
| same audit plus TCP port probes on private-LAN devices you own |
| private-LAN inventory only |
| signals behind the last check |
| spoken verdicts and macOS alerts |
| reply language and number region |
| settings and help |
Memory. Every console check is stored in ~/.scamcheck/history.db (SQLite,
mode 0600, local only). Before recording, scamcheck looks up the link hosts,
phone numbers and an exact-message hash, so a repeat gets a "seen before" line
and a new link/number gets "new to me". Message text is stored to make search
and why useful; history clear wipes it. Nothing is synced or uploaded.
Voice and alerts. --voice uses macOS say; --notify uses Notification
Center. Both fire only for SUSPICIOUS or worse by default (SCAMCHECK_ANNOUNCE_ALL=1
announces safe results too). Set SCAMCHECK_VOICE_NAME (or
SCAMCHECK_VOICE_NAME_HI for Hindi) to pick a system voice. The Hindi voice is
not installed on every Mac; English always works. On other platforms the
console still runs, just without local speech/notification.
Link investigation is the only online tool. url intentionally fetches the
link (redirects, certificate) and asks rdap.org for the registration date —
only when you type the command. Private/loopback targets are refused, including
hostnames that resolve to private addresses. A normal text/screenshot/QR check
never uses the network. AI stays off here by default; set
SCAMCHECK_JARVIS_AI=auto|always plus an API key if you want the optional
second opinion.
Ambient screenshots. --watch --screenshot-dir ~/Desktop watches both the
clipboard and that folder. On startup it checks the 10 newest images already
there, then every new or changed screenshot. Combine with --voice/--notify.
Watch results go into the same local memory.
Watch checks stay local even if an AI key is configured: the watcher forces
ai=never unless you explicitly opt in with SCAMCHECK_WATCH_AI=auto|always
(a literal --ai never always wins). This is deliberate — an ambient loop
should not upload whatever happens to be copied.
Cases, watchlists and recovery
A case is a private investigation folder for one scam. Every text or file is hashed with SHA-256 at collection time; case exports include a hashes file so you can show that evidence was not changed afterwards.
# open a case (case IDs look like C-20261024-4A7F)
scamcheck --case-new "Bank transfer fraud" --scam-type bank \
--country IN --currency INR --loss 25000 --case-notes "first contact 10:20"
# attach screenshots, chat exports, PDFs or pasted text
scamcheck --case-add C-20261024-4A7F ~/Desktop/whatsapp.png
scamcheck --case-add C-20261024-4A7F "He asked me to send USDT to T..."
# record a suspect identifier or a timeline note
scamcheck --case-entity C-20261024-4A7F "wallet:T..."
scamcheck --case-note C-20261024-4A7F "Bank complaint reference 99881"
# inspect, check for tampering, and export
scamcheck --case-list
scamcheck --case C-20261024-4A7F
scamcheck --case-verify C-20261024-4A7F
scamcheck --case-export C-20261024-4A7F --case-out ~/Desktop/case-pack
scamcheck --case-close C-20261024-4A7F
# complaint draft (edit it before submitting; this is not a legal filing)
scamcheck --report C-20261024-4A7F --country IN --report-out complaint.txtAn export contains case.json, case.md, case.html, SHA256SUMS and an
evidence/ folder. A PDF is generated only when weasyprint, wkhtmltopdf
or a local Chrome/Chromium is installed; otherwise use the HTML/Markdown.
Watchlists warn you the next time a known indicator appears in any check:
scamcheck --watch-add "upi:scammer@okhdfcbank"
scamcheck --watch-add "url:evil-example.xyz"
scamcheck --watch-add "+91 98765 43210"
scamcheck --watchlist
scamcheck --watch-remove "url:evil-example.xyz"Matches appear in human output and in --json under watchlist (stdout stays
valid JSON). Supported kinds include phone, url, upi, email, telegram,
whatsapp, crypto_btc, crypto_eth, crypto_trx, ifsc and apk.
Campaigns group prior checks that share a phone, domain, UPI ID, wallet, handle or APK — so one victim's report becomes an early warning for the next:
scamcheck --campaigns
scamcheck --campaign CAM-00042Recovery playbooks give an ordered, country-aware checklist (immediate, today, this week) and the right reporting channels:
scamcheck --recover upi --country IN
scamcheck --recover crypto --country US
scamcheck --recover job --country GBSupported country profiles: IN, US, GB, AU, CA, SG, AE, NZ, ZA (generic fallback elsewhere). Hotlines and portals change — verify them on the official site before sharing personal details. These playbooks are guidance, not legal advice, and the evidence pack is for lawful reporting, not self-prosecution.
Device & LAN audit (--device-audit)
A read-only security check for the machine scamcheck runs on, plus an inventory of the private network it is attached to. It is not an antivirus engine: it checks the things a careful person would check, without sudo, writes or exploitation.
python3 -m scamcheck --device-audit # local audit + passive LAN inventory
python3 -m scamcheck --device-audit --device-full # print every section
python3 -m scamcheck --device-audit --device-active # + TCP port probes on your private LAN
python3 -m scamcheck --device-audit --device-out DIR # choose the export folder
python3 -m scamcheck --device-audit --device-no-lan --device-no-mobile
python3 -m scamcheck --device-audit --json # report + export paths as JSONWhat it looks at (macOS first; other platforms skip unsupported checks):
Posture: SIP, FileVault, Gatekeeper, firewall, Remote Login, MDM, guest/automatic login, update settings, XProtect/MRT versions.
Processes: reverse shells, miners,
curl | sh, temp/shared-path binaries, Apple-lookalike names outside Apple paths, sustained high CPU.Persistence: LaunchAgents/Daemons, login items, background task items, cron/
at, shell profiles, SSH authorized keys, configuration profiles.Browsers: Chromium-family and Firefox extensions with high-risk permissions, native-messaging hosts, Safari extensions, managed policies.
Network: interfaces, DNS, proxy, custom
/etc/hosts, listening services, established connections and unusual remote ports.Files: recent downloads, quarantine events, executables in temp/shared folders, unsigned recently-changed applications.
Extensions: system extensions and third-party kernel extensions.
Mobile: connected Android (only with an authorized
adb) and iOS inventory; per-platform checklists when no agent exists.LAN: devices already visible in the ARP table, vendor lookup, device guessing, and optional TCP connects to common ports.
Every run writes report.json, report.md and SHA256SUMS to
~/.scamcheck/device-reports/<timestamp>/ (mode 0700); --device-out picks
another folder. Reports stay local. --device-active is opt-in, only connects
to RFC1918/link-local IPv4 addresses, caps the host list at 64, and never
attempts logins, exploitation, packet capture or public-IP scanning.
MCP server
scamcheck --mcp speaks newline-delimited JSON-RPC 2.0 on stdin/stdout (the
MCP stdio transport). stdout carries protocol frames only; diagnostics go to
stderr. Tools are listed in the MCP section
above; the implementation is scamcheck/mcp.py and the contract is covered by
tests/test_mcp.py.
Design choices worth knowing:
check_messagedefaults toai: "never"so an agent cannot silently make a network call on a victim's message.check_urlis the only networked tool, and it refuses private, loopback and link-local targets (including via redirect).Tool failures are returned as
isError: truewith a readable message, so the model can recover; protocol failures stay JSON-RPC errors.open_casewrites to the same~/.scamcheckstore as the CLI, so a case opened by an agent can be exported with--case-export.
Local API
python3 -m scamcheck --serve (CORS-enabled, localhost only):
Endpoint | Body | Returns |
| — | family web UI (HTML) |
|
| full verdict payload |
|
| number intel + reputation + risk |
|
| verdict + |
| — |
|
What it can't do (yet)
Always-listening "Hey JARVIS" wake word or call monitoring — the console, clipboard watcher and screenshot folder are the ambient surfaces for now
Voice calls — feed it a call transcript and it already works; automatic call-log speaker-to-text would need OS-level integration
A bundled global spam-number database — no free, trustworthy bulk feed exists; the tool is feed-agnostic and starts empty (import your own lists)
Trained on synthetic screenshots only — real chat-app screenshot quirks (dark mode, emoji-heavy text, mixed scripts like Devanagari/Tamil) may need engine-specific tweaks
URL investigation is not a malware sandbox: it follows public HTTP(S) redirects and reads metadata, it does not execute or download files
Case packs are an investigative aid, not a legal filing; court-ready PDF output needs a local HTML→PDF converter, and the vault is unencrypted at mode
0600(full-disk encryption is your friend)Campaign clustering and watchlists are local to this machine; there is no shared/community indicator database
Device audit is not an antivirus: no signature engine, memory scanning or quarantine/remediation. It reports suspicious state; you decide what to remove
iOS/iPadOS/tvOS/Android cannot be deeply inspected from this Mac without a per-device agent or authorized ADB; remote devices get inventory + checklists
LAN checks are connect-only and depend on ARP visibility; they do not exploit, log in, capture packets or scan public IP addresses
Memory is per-machine and unencrypted (mode
0600); there is no cloud syncIntelligence labels and next-move text are English-first; Hindi/Hinglish detection still works through the rules and conversation layers, but the briefing prose is not fully translated yet
MCP exposes analysis, numbers, entities and cases — not OCR, the bots or the device audit; those stay on the CLI where a human is in the loop
It's a helper, not gospel — always double-check urgent messages with the official app or hotline before acting
Layout
scamcheck/
├── scamcheck/ # the Python package (rules, analyzer, AI, CLI)
│ ├── __init__.py
│ ├── __main__.py # python3 -m scamcheck entry
│ ├── cli.py
│ ├── analyzer.py
│ ├── intelligence.py # scam type, stage, goals, next moves, deobfuscation
│ ├── ocr.py # screenshot OCR + QR decode + clipboard helpers
│ ├── conversation.py # chat-trajectory engine
│ ├── scripts_lib.py # scam playbook fingerprints
│ ├── intel.py # shared identifier/entity extraction
│ ├── cases.py # evidence cases + SHA-256 custody
│ ├── watchlist.py # local indicator watchlist
│ ├── campaigns.py # campaign clustering over history
│ ├── recovery.py # country-aware recovery + complaint templates
│ ├── api.py # local REST API + web UI server (--serve)
│ ├── mcp.py # MCP stdio server (--mcp)
│ ├── webui.py # family web UI (single-file HTML/CSS/JS, EN/HI)
│ ├── webcli.py # JSON bridge used by the Next.js interface
│ ├── phones.py # number normalization, local lists, opt-in feeds
│ ├── bot.py # Telegram long polling + WhatsApp Cloud API webhook
│ ├── jarvis.py # console, local memory, voice/alerts, tool router
│ ├── deviceaudit.py # read-only device + private-LAN security audit
│ ├── netintel.py # on-demand URL redirects/TLS/RDAP investigation
│ ├── rules.py # the heuristic engine (tune here)
│ └── ai.py
├── tests/
│ ├── samples.py # curated scam & legit specimens
│ ├── make_png.py # synthetic screenshot generator (stdlib-only)
│ ├── test_ocr.py # OCR pipeline honors test (macOS / tesseract)
│ ├── test_batch.py # --batch splitting + CLI behavior
│ ├── test_api_ui.py # API + family UI page/endpoints
│ ├── test_mcp.py # MCP stdio protocol + tool contract
│ ├── test_phones.py # number parsing, lists, feeds, CLI, API
│ ├── test_bot.py # bots: formatting, commands, Telegram + WhatsApp
│ ├── test_cases.py # cases, watchlists, campaigns, recovery
│ ├── test_intelligence.py # deobfuscation + intel briefing + routes
│ ├── test_deviceaudit.py # device audit guards, export, CLI + JARVIS routes
│ ├── test_webcli.py # JSON bridge contract, tools, errors, streaming
│ ├── test_jarvis.py # memory, intents, URL/QR/image tools, voice, CLI
│ └── run_samples.py # regression self-test
├── web/ # Next.js interface + documentation site
│ ├── app/ # pages, docs and API routes
│ ├── components/ # workbenches, shell, verdict panels
│ ├── lib/ # Python bridge spawner and shared types
│ └── package.json
├── pyproject.toml # packaging metadata + `scamcheck` entry point
├── setup.py # shim for Apple's old pip/setuptools
└── README.mdPlus ~/bin/scamcheck — a launcher that turns this into a global command.
Available Tools
6 toolscheck_messageCheck a message for scam signalsA
Analyze an SMS, chat message, email body or full conversation transcript for scam signals. Runs fully offline and returns an explainable verdict (LIKELY SAFE / SUSPICIOUS / LIKELY SCAM / SCAM) with a 0-100 score, the exact matched signals, the matched scam playbook, and a deterministic intelligence briefing (scam type, kill-chain stage, attacker goals, payment rail, likely next moves, verification steps). Use this before advising a user to act on any unsolicited message.
| Name | Required | Description | Default |
|---|---|---|---|
| ai | No | Consult the optional LLM second opinion. Defaults to 'never' so the tool stays offline and deterministic. | never |
| text | Yes | The message or transcript to analyze. | |
| region | No | Default region for national numbers, e.g. IN, US, GB. | |
| sender | No | Optional originating phone number or SMS sender ID (e.g. '+919876543210' or 'VM-HDFCBK'). Reputation is read from local lists and cached feeds only. | |
| conversation | No | Treat the text as a multi-turn transcript and score the kill-chain trajectory. Auto-detected for multi-line input when omitted. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and discloses that analysis runs fully offline and deterministically by default, and returns an explainable verdict, score, matched signals, playbook, and briefing. It does not mention side effects, permissions, limitations, or how the optional ai second opinion affects offline behavior, leaving minor gaps.
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 main purpose and remains reasonably efficient. The output-detail sentence is long but earns most of its place by documenting return values, which is necessary because there is no output schema.
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?
With no output schema, the description fully explains the return values: verdict levels, 0-100 score, matched signals, playbook, and intelligence briefing. It also covers input types and offline/deterministic behavior, so an agent has enough context to invoke it correctly.
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 schema already explains text, ai, region, sender, and conversation. The description only echoes the transcript use case and adds no real parameter semantics beyond the structured schema, so baseline 3 applies.
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 specific verb and resource: analyze SMS, chat messages, email bodies, or transcripts for scam signals. This clearly distinguishes check_message from siblings like check_number and check_url, which operate on different artifacts.
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?
Explicitly says to use it before advising a user to act on any unsolicited message, giving a clear usage context. It does not name when to prefer check_number, check_url, or other siblings, so the routing guidance is good but not complete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_numberCheck a phone numberA
Parse and classify a phone number or SMS sender ID offline: validity, E.164 form, country, line type, premium-rate ranges, country mismatch, and local blocklist/allowlist reputation. No network access.
| Name | Required | Description | Default |
|---|---|---|---|
| number | Yes | Number or sender ID. | |
| region | No | Region for national numbers, e.g. IN. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does meaningful work: it discloses that the check is fully offline with no network access, and that it is a classification/read operation returning reputation signals rather than mutating anything. It stops short of stating whether blocklist/allowlist results depend on server-side configuration or how results are shaped, but the safety and execution profile is clear.
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?
A single front-loaded sentence led by the verb, with the 'offline / no network access' qualifier placed at the end as a compact caveat. Every clause adds discriminating information; nothing is wasted.
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?
There is no output schema and no annotations, so the description must convey what comes back, and it does by enumerating the classification dimensions. It is nearly complete for a two-parameter read tool; only the precise result shape and any config-dependent behavior are left implicit.
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 both parameters are already documented, which sets the baseline at 3. The description does add a little signal by noting sender IDs are accepted and that country/line-type classification depends on a region for national numbers, but it never explains the region parameter's role explicitly.
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 uses a specific verb pair ('parse and classify') on a clearly scoped resource ('a phone number or SMS sender ID') and enumerates the outputs (validity, E.164, country, line type, premium-rate, reputation). This distinguishes it from siblings like check_url and check_message, which operate on different artifacts.
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?
Usage is implied rather than stated: an agent can infer this is the right tool when it holds a raw number or sender ID, and 'offline / no network access' signals it is a fast local check. However, there is no explicit guidance on when to prefer check_message or check_url instead, nor any note on required vs. optional region usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_urlInvestigate a link (network)A
Fetch public HTTP(S) metadata for one link: redirect chain, final URL and page title, TLS issuer/version, RDAP domain age, and flags for plain HTTP, raw IP hosts, punycode lookalikes and shorteners. This is the ONLY scamcheck tool that touches the network. Private, loopback and link-local targets are refused. It reads metadata; it is not a sandbox and does not execute page content.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Public http(s) URL. | |
| rdap | No | Look up domain registration data (RDAP). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: it discloses the outbound network side effect, the SSRF-style guard that refuses private, loopback and link-local targets, and the crucial safety boundary that it reads metadata only and does not execute page content or act as a sandbox. These are exactly the traits an agent needs before calling an outward-facing fetch.
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?
Three tight sentences with zero padding; the capability and its returned fields come first, followed by the scoping/safety constraints. Every clause earns its place.
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?
There is no output schema, and the description compensates by naming the returned fields (redirect chain, final URL, title, TLS, RDAP age, flags). Combined with the stated safety boundaries and refusal behavior, an agent has everything needed to invoke it correctly and interpret the result.
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 coverage is 100%, so both parameters are already documented, warranting the baseline 3. The description does reinforce that the target must be a public HTTP(S) URL, but it never mentions that the 'rdap' lookup can be disabled — it only lists RDAP domain age as a return field, leaving the toggle's effect to the schema.
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 gives a specific verb and resource ('Fetch public HTTP(S) metadata for one link') and then enumerates exactly what comes back: redirect chain, final URL, title, TLS issuer/version, RDAP age, and heuristic flags. It explicitly separates itself from the other scamcheck tools by stating it is the ONLY one that touches the network.
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 'ONLY scamcheck tool that touches the network' line tells an agent when this tool is the right pick over check_message, check_number, and extract_entities for a link. The refusal of private/loopback/link-local targets is a clear negative condition. However, it never says when not to use it (e.g., for a bare domain vs a full URL) or how it relates to extract_entities for link discovery.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
extract_entitiesExtract indicators from textA
Extract stable, deduplicated indicators from a scam message or case: URLs/domains, phone numbers, sender IDs, emails, UPI IDs, BTC/ETH/TRX wallets, IFSC codes, Telegram handles, WhatsApp invites and APK links. Offline; useful for building blocklists or a case file.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | Message or case text. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does well: it discloses that the tool is offline (no external lookups or data exfiltration), that results are stable and deduplicated (idempotent output), and what categories are returned. It omits the concrete return shape (e.g., list vs. grouped-by-type) and any error behavior for text with no indicators.
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 sentences, no filler. The indicator enumeration is front-loaded and the trailing clause adds use-case value rather than restating the tool name.
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 single-parameter, annotation-free, no-output-schema tool, the description covers input domain, extraction scope, and output character (stable, deduplicated). The remaining gap is return structure, but with no output schema and a self-evident list-of-indicators result, this is minor.
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?
Only one parameter with 100% schema description coverage, so the baseline is 4 by rule. The description adds domain framing ('a scam message or case') that clarifies what kind of 'text' is appropriate, which is a small value-add over the schema's 'Message or case text.'
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 specific verb ('extract') and resource ('stable, deduplicated indicators') and then enumerates the exact indicator classes (URLs/domains, phone numbers, wallets, IFSC, Telegram handles, etc.). This makes it immediately distinguishable from siblings like check_url or check_message, which validate rather than extract.
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?
Gives clear usage context: input is a scam message or case, and the output is useful for building blocklists or a case file. It does not explicitly name alternatives (e.g., 'use check_url to score a URL you already have') or state when not to use it, so it stops short of the top band.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_caseOpen an evidence caseA
Open a local evidence case and optionally attach the analysed text. Evidence is SHA-256 hashed at collection time and stored under ~/.scamcheck (mode 0700). Use this when a user wants to report a scam or preserve proof; the case can later be exported with the CLI (--case-export) or turned into a complaint draft (--report).
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | Opening notes. | |
| title | Yes | Short case title. | |
| currency | No | e.g. INR, USD. | |
| scam_type | No | e.g. upi, bank, crypto, job, romance, sextortion. | |
| loss_amount | No | Reported loss, if any. | |
| evidence_text | No | Message text to attach as hashed evidence. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that evidence is SHA-256 hashed at collection time, persisted to ~/.scamcheck with mode 0700, and is local-only. It omits whether re-opening/duplicate cases are possible, what identifier or path is returned, and any failure modes (disk, permissions).
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 sentences, zero filler: the first covers what happens and where data lands, the second covers when to use it and what the artifact is good for. Storage and hashing are front-loaded before the usage guidance.
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?
A six-parameter mutation tool with no annotations and no output schema — the description supplies storage location, permissions, integrity mechanism (hashing), and downstream export paths, which is most of what an agent needs. The notable remaining gap is what the call returns (case ID or path) so the agent can reference the case later.
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 all six parameters are already documented; baseline is 3. The description adds only marginal meaning by indicating evidence_text is optional and gets hashed, and says nothing about currency, loss_amount, scam_type, or notes.
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 specific verb and resource — 'Open a local evidence case' — plus the optional attachment of analysed text, which is clearly distinct from the read-only checking siblings (check_number, check_message, check_url). It doesn't explicitly name a sibling as the alternative path, so the differentiation is inferred rather than stated.
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?
'Use this when a user wants to report a scam or preserve proof' gives an explicit trigger condition, and the follow-on paths (--case-export, --report) tell the agent what the case is for downstream. No when-not guidance or comparison to the check_* tools, but the usage context is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scamcheck_infoServer capabilitiesA
Return the scamcheck version, whether analysis is offline, where local data lives, and which capabilities this build has.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does reasonably well: 'Return' implies a pure read with no side effects, and it enumerates the specific facts disclosed (version, offline flag, data path, capability set). It does not mention auth requirements, cost, or whether the data path is sensitive, but for a zero-parameter local info call the remaining gap is small.
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?
A single sentence, front-loaded with the verb and then the four return fields in priority order. No filler, no restatement of the title.
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?
There is no output schema, so the description must enumerate return values, and it does list all four. The one soft spot is 'which capabilities this build has,' which gives no hint of the shape or vocabulary of that list, leaving the agent unsure what it will be able to branch on.
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 tool takes zero parameters, so there is nothing to disambiguate; baseline 4 applies. The description correctly does not waste words on inputs.
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 specific verb ('Return') plus the exact resources returned: version, offline status, local data location, and available capabilities. It is clearly a metadata/introspection tool, which implicitly separates it from the action-oriented siblings (check_url, check_number, check_message, extract_entities, open_case), though it never names them explicitly.
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?
There is no statement of when an agent should call this versus the check_* tools, nor any precondition such as 'call this first to discover whether offline mode is available.' The reader must infer the diagnostic/discovery use case from the returned fields alone.
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.
6 tool updates
v0.13.0- First observed
check_message - First observed
check_number - First observed
check_url - First observed
extract_entities - First observed
open_case - First observed
scamcheck_info
TDQS
Scored across 6 tools
Each tool targets a distinct action and input type: check_number for phone numbers, check_message for message verdicts, check_url for URL metadata, extract_entities for indicator extraction, open_case for evidence cases, and scamcheck_info for server metadata. Although check_message and extract_entities both accept message text, their outputs and use cases are clearly differentiated in the descriptions.
Five of six tools follow a clear verb_noun pattern (check_number, extract_entities, open_case, check_message, check_url). The outlier scamcheck_info is a noun-based info tool, which is a minor deviation but still readable and predictable.
Six tools is well-scoped for an offline scam analysis server. Each tool covers a necessary capability—message analysis, number check, URL check, entity extraction, case opening, and info—without redundancy or bloat.
The core scam-analysis surface is complete for messages, phone numbers, URLs, and indicator extraction. However, case management is a dead end in the MCP layer: open_case exists but there is no tool to retrieve, list, or export cases (those functions are only in the CLI).
Maintenance
Related MCP Connectors
Email safety MCP server. Detects phishing, prompt injection, CEO fraud for AI agents.
Context and control for any AI. Agent identity, scoped access, and proof across tools.
Email OS for agents - real-inbox search, triage, commitments, and a verifiable BEC hard-stop.
A paid remote MCP for Context7 MCP docs, built to return verdicts, receipts, usage logs, and audit-r
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceModel Context Protocol server that connects AI agents with CrowdStrike Falcon platform for intelligent security analysis and automation.MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI clients to submit files, hashes, and URLs for malware analysis, retrieve results, search across AssemblyLine indices, and triage alerts, all through the Model Context Protocol.MIT
- AlicenseNot gradedqualityAmaintenanceEnables secure enterprise AI agents to access internal tools like GitHub, Gmail, Calendar, file systems, databases, and knowledge bases through the Model Context Protocol, with built-in security, audit, and observability.MIT

Aribot MCPofficial
AlicenseNot gradedqualityDmaintenanceEnables security work such as threat modeling, scanning, compliance checks, and remediation through AI assistants using the Model Context Protocol.MIT