Skip to main content
Glama
Ctrlanmol

scamcheck

by Ctrlanmol

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.

CI Python Dependencies Network License

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, export

Why it is different

  1. 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.

  2. Offline by default. Analysis never opens a socket. The only networked actions are explicit and opt-in: --update-feeds, url <link>, check_url over MCP, the chat bots, and an AI key you set yourself.

  3. Evidence-grade. Cases hash every artefact at collection time (SHA-256), record a timeline, extract entities, and export a pack with a SHA256SUMS file. --case-verify detects 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 --version

There 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

check_message

no

verdict, score, signals, playbook, full intelligence briefing

check_url

yes

redirect chain, TLS, RDAP age, punycode/raw-IP flags

check_number

no

E.164, country, line type, premium ranges, local reputation

extract_entities

no

phones, UPI IDs, wallets, domains, emails, handles, APK links

open_case

no

a local case with SHA-256 hashed evidence

scamcheck_info

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 --mcp

Prefer 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.py

Who it is for

You are

You get

A developer

--json, stable exit codes, a local HTTP API, an MCP server. Filter contact-form spam or support tickets without a vendor.

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

--batch over an inbox export, --campaigns to cluster, --report for complaint drafts.

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

--watch --voice --notify, a LAN web UI, and a Telegram bot.

An AI agent builder

--mcp: six tools, one of which touches the network.

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. url reads 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/0700 plaintext. Use FileVault or LUKS for sensitive cases; encrypted vaults are planned.

  • Not authenticated. --serve --host 0.0.0.0 is 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

docs/LAUNCH.md

Market research, competitive positioning, launch plan, 90-day roadmap

docs/ROADMAP.md

Shipped history and planned work, version by version

docs/scam-research-dossier.md

Scam taxonomy and 2025–26 research base

SECURITY.md

Guarantees, limits, safe evidence handling

CONTRIBUTING.md

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:

  1. 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

  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 --intel below.

  3. 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.

  4. 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 Anthropic

No 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:

  1. Apple Vision (macOS 10.15+): built in, zero installs

  2. Tesseract: if tesseract is on PATH (brew install tesseract)

  3. 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 transcript

Each 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 screenshots

  • SHORTENERS, RISKY_TLDS, BRANDS — extend the link forensics lists

  • verdict 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" --json

It understands:

  • premium-rate numbers — IN 1900, US/CA 900, UK 09, AU 190x: calling or texting them costs extra

  • SMS shortcodes and alphanumeric sender IDs (VM-HDFCBK) — not personal lines, and sender IDs are spoofable

  • country mismatch — e.g. a +1 personal number claiming to be HDFC while the message talks in ₹/UPI/KYC terms

  • your blocklist / allowlist and cached feed reports

Everything lives under ~/.scamcheck/ (override with SCAMCHECK_HOME):

File

Purpose

blocklist.txt

numbers you marked as scams (one per line, optional ,label)

allowlist.txt

numbers you trust — wins over the blocklist

feeds.json

cache written only by an explicit --update-feeds

feeds.conf

the public list URLs you choose

config.json

default region

history.db

JARVIS local memory (SQLite, 0600)

cases.db

evidence-case metadata, entities, evidence hashes and timeline (0600)

cases/

private evidence vault: one folder per case (0700, files 0600)

exports/

default case-pack exports (JSON/MD/HTML/SHA256SUMS, optional PDF)

watchlist.db

local watchlist identifiers + hit counters (0600)

jarvis_prefs.json

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 flags

Feeds (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 | csv
scamcheck --update-feeds       # the ONLY command that touches the network
scamcheck --feed-status
  • HTTPS only (plain HTTP allowed only for 127.0.0.1 self-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.0

That 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:3000

Production build:

cd web
npm run build
npm start

Requirements: 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

/check

Message, conversation, screenshot, batch, link and QR

Numbers & feeds

/numbers

Number intel, local block/allow lists, imports, feeds

JARVIS

/jarvis

The terminal command grammar in a chat console

Evidence cases

/cases

Create cases, add hashed evidence, verify, export, report

Watchlist

/watchlist

Watch phones, domains, UPI IDs, wallets and handles

Campaigns

/campaigns

Related checks clustered by shared indicators

Recovery

/recovery

Country-aware playbooks for 15 scam types

Device audit

/device

Read-only machine + private-LAN audit, streamed live

Settings

/settings

Region, local memory, bot status, data files

Documentation

/docs

Overview, install, CLI, web, API, device, cases, privacy, FAQ

How the bridge works

  • POST /api/tool takes {"tool": "...", "args": {...}}, validates the tool against a whitelist, spawns python3 -m scamcheck.webcli in the project root and returns {"ok": true, "data": ...} or an error.

  • POST /api/device-audit streams progress over Server-Sent Events, then a single result event with the report and export paths.

  • The bridge finds the project root by walking up from web/ looking for scamcheck/__init__.py and pyproject.toml. Override it with SCAMCHECK_ROOT if the app runs from elsewhere.

Environment variables

Variable

Used by

Purpose

SCAMCHECK_HOME

Core + web

Data directory (default ~/.scamcheck)

SCAMCHECK_REGION

Core + web

Default region, e.g. IN

SCAMCHECK_ROOT

Web

Project root containing scamcheck/

SCAMCHECK_PYTHON

Web

Python binary to spawn (default python3)

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

  1. Create a bot with @BotFather and copy the token.

  2. 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

/lang en / /lang hi

reply language (per chat, remembered)

/sender +91 98765 43210

remember a sender number for this chat

/sender off

forget it

/check <text>

check one message explicitly

/status

region, language, sender, cached feed count

/help

usage

Limits and privacy:

  • 20 checks/hour/chat by default (SCAMCHECK_BOT_RATE), so a group can't spam it

  • text/screenshots are analyzed locally; nothing is sent to an AI unless you set SCAMCHECK_BOT_AI=auto|always and an API key

  • Telegram doesn't expose the original sender number when you forward a message, so set /sender manually when that matters

  • the bot process must stay running; long polling needs no ports or certificates

WhatsApp — Cloud API + public HTTPS webhook

  1. Create a Meta app with the WhatsApp product and a test number. Collect WHATSAPP_TOKEN and WHATSAPP_PHONE_NUMBER_ID.

  2. 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/whatsapp
  1. Give Meta a public HTTPS URL with a tunnel:

cloudflared tunnel --url http://127.0.0.1:8766
  1. In Meta → WhatsApp → Configuration, set the callback URL to https://<your-tunnel-host>/webhook/whatsapp and the verify token to the same value. Subscribe to messages.

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_SECRET enables X-Hub-Signature-256 verification on every POST

  • bind 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 --jarvis

Command

What it does

check <text> (or just paste text)

analyze an SMS/email/chat message

assess <text>

full intelligence brief: type, stage, goals, next moves, verification, evidence

brief / next / reply

briefing, attacker next moves, or safe replies for the last check

image <path>

OCR a screenshot, decode QR codes, then analyze

qr <path>

decode QR/barcodes only; never opens them

paste

check the current clipboard (text or screenshot)

url <link>

redirect chain, TLS, domain age, final page title

number <phone>

range facts, premium-rate, local lists, cached feeds

block/allow/forget <phone> [label]

edit your local number lists

sender <phone|off>

attach a sender number to subsequent checks

history [n] / search <term> / history clear

local memory

case ...

list, new <title>, show, add, note, entity, export, verify, close

watch <KIND:VALUE> / unwatch <KIND:VALUE> / watchlist

local watchlist

campaigns [n] / campaign <id>

cluster related checks by shared indicators

recover [type]

country-aware recovery playbook

report <case-id>

draft a complaint from a case

scan device / scan device full

read-only device audit (posture, processes, persistence, browsers, network, files)

scan device active

same audit plus TCP port probes on private-LAN devices you own

scan network [active]

private-LAN inventory only

why

signals behind the last check

voice on|off, notify on|off

spoken verdicts and macOS alerts

lang en|hi, region IN|US|...

reply language and number region

feeds, status, help, quit

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.txt

An 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-00042

Recovery 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 GB

Supported 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 JSON

What 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_message defaults to ai: "never" so an agent cannot silently make a network call on a victim's message.

  • check_url is the only networked tool, and it refuses private, loopback and link-local targets (including via redirect).

  • Tool failures are returned as isError: true with a readable message, so the model can recover; protocol failures stay JSON-RPC errors.

  • open_case writes to the same ~/.scamcheck store 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

GET /

—

family web UI (HTML)

POST /v1/analyze

{"text": "..."} or {"conversation": "..."}, optional "sender", "region", "ai": "always"

full verdict payload

POST /v1/check-number

{"number": "+91 98765 43210"}, optional "region"

number intel + reputation + risk

POST /v1/check-image

{"path": "..."} or {"image_b64": "..."}

verdict + ocr_engine + extracted_text + qr (when codes are present)

GET /health

—

{"ok": true, "version": "0.13.0"}

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 sync

  • Intelligence 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.md

Plus ~/bin/scamcheck — a launcher that turns this into a global command.

Available Tools

6 tools
check_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.

NameRequiredDescriptionDefault
aiNoConsult the optional LLM second opinion. Defaults to 'never' so the tool stays offline and deterministic.never
textYesThe message or transcript to analyze.
regionNoDefault region for national numbers, e.g. IN, US, GB.
senderNoOptional originating phone number or SMS sender ID (e.g. '+919876543210' or 'VM-HDFCBK'). Reputation is read from local lists and cached feeds only.
conversationNoTreat the text as a multi-turn transcript and score the kill-chain trajectory. Auto-detected for multi-line input when omitted.

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault
numberYesNumber or sender ID.
regionNoRegion for national numbers, e.g. IN.

TDQS

A4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

NameRequiredDescriptionDefault
urlYesPublic http(s) URL.
rdapNoLook up domain registration data (RDAP).

TDQS

A4.5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault
textYesMessage or case text.

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

NameRequiredDescriptionDefault
notesNoOpening notes.
titleYesShort case title.
currencyNoe.g. INR, USD.
scam_typeNoe.g. upi, bank, crypto, job, romance, sextortion.
loss_amountNoReported loss, if any.
evidence_textNoMessage text to attach as hashed evidence.

TDQS

A4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

  1. 6 tool updatesv0.13.0
    • First observedcheck_message
    • First observedcheck_number
    • First observedcheck_url
    • First observedextract_entities
    • First observedopen_case
    • First observedscamcheck_info

TDQS

A4.1/5.0

Scored across 6 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables 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