SnagrSAFE
Selfhosted price tracker that searches marketplaces. A agent uses a headless browser to find, as well as recheck listings with a fastAPI backend and a live updating react dashboard
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
[](https://github.com/oldmoldycake/Snagr/actions/workflows/ci.yml) [](https://github.com/oldmoldycake/Snagr/actions/workflows/codeql.yml) [](https://github.com/oldmoldycake/Snagr/releases) [](LICENSE)
A self-hosted price tracker for secondhand-marketplace hunting. You describe what you're watching for and at what price; an always-on agent drives a real headless browser to find new listings and re-check known ones, and the web UI shows every hunt's state at a glance — current best price, drift against your target, price history, and what the hunter is doing right now.
Status: early (0.x). The whole stack runs end to end, every push to main publishes container images, and release-please cuts a tagged release from the CHANGELOG. Expect rough edges, and read the release notes before upgrading between minor versions.
All three screenshots are the frontend's built-in mock data (see Development).
Contents
- Features
- How it works
- Requirements
- [Quick start](
3afec73517f6OBSERVED · 2026-10-11Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add frontend --env AI_API_KEY=${AI_API_KEY} --env BRAVE_API_KEY=${BRAVE_API_KEY} --env HF_TOKEN=${HF_TOKEN} --env JWT_SECRET=${JWT_SECRET} -- npx -y [email protected]{
"mcpServers": {
"frontend": {
"command": "npx",
"args": [
"-y",
"[email protected]"
],
"env": {
"AI_API_KEY": "${AI_API_KEY}",
"BRAVE_API_KEY": "${BRAVE_API_KEY}",
"HF_TOKEN": "${HF_TOKEN}",
"JWT_SECRET": "${JWT_SECRET}"
}
}
}
}Exposed tools (33)
21 read · 7 write · 5 destructive. Blast radius: 5 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
cancel_job | read | Cancel one of your pending or running hunts (admins: any job). A |
confirm_review_entry | read | Give the human verdict on a queued photo: label it |
create_category | write | Add a category, e.g. |
create_item | write | Start watching an item. If the shared catalog already has an item of |
create_site | write | Add a marketplace the agent can search, e.g. name |
delete_category | destructive | Delete a category AND everything under it: its items, every user |
delete_item | destructive | Stop watching an item: removes this user |
delete_site | destructive | Delete a site (id or name). It is unlinked from every category, |
discard_review_entry | destructive | Drop a queued photo without a verdict — it is neither real nor fake |
enqueue_jobs | read | Ask the hunter for something now, and get back the jobs it queued. |
get_category_price_change | read | Per-item best-price percent change over `range` for every item this |
get_dashboard_stats | read | The dashboard tiles for this user over `range`: tracked items, active |
get_instance | read | What this Snagr instance has switched on: its version, whether photo |
get_item | read | One watched item in full: everything list_items shows plus every |
get_job | read | One job with the last 50 lines of its log — what the hunt searched, |
get_price_drops | read | The biggest recent price drops across everything this user watches — |
get_price_history | read | One price series per listing of a watched item over `range`, plus |
get_price_summary | read | The average and best price of a watched item over time, bucketed |
jobs_summary | read | What the hunter is doing right now, in one object: how many hunts |
list_categories | read | Every category (video games, trading cards, ...) with its slug, the |
list_items | read | The items this user watches, each with its target price, current best |
list_jobs | read | The hunter |
list_listings | read | Listings across every item this user watches, newest first — the way |
list_price_checks | read | The raw observations behind an item |
list_references | read | An item |
list_review_queue | read | Listing photos the agent captured on this user |
list_sites | read | Every marketplace the agent can search, with its base URL, the |
revoke_reference | destructive | Retire a reference photo from an item |
update_category | write | Rename a category and/or replace the set of sites it is searched on — |
update_item | write | Change a watched item |
update_listing | write | Stop tracking a listing (active=false: it is no longer re-checked |
update_site | write | Rename a site or change its base URL (`site` is an id or name). |
whoami | read | The account this token acts as, and the scopes it carries (read, |
Trust audit
SAFEgrade B · trust 86/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | PASS |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (4 observation(s))
- Network
- declared (5 observation(s))
- Shell
- none-observed
- Dependencies
- not all pinned
- Secrets in source
- none-found
Findings (24)
delete_category, delete_item, delete_site, discard_review_entry, revoke_reference
.release-please-manifest.json
.env.development
.oxlintrc.json
const token = '../../admin/invites#'
const token = '../../admin/users#'
"http://169.254.169.254/latest/meta-data/",
"169.254.169.254",
"http://169.254.169.254/latest/meta-data/",
channel_id = await _saved_before_the_guard(db_session, user_id, "http://169.254.169.254/")
({"kind": "webhook", "url": "http://169.254.169.254/latest/meta-data/"}, "url"),PLAYWRIGHT_MCP_URL=http://192.168.1.10:8931/mcp
monkeypatch.setattr(tools, "VISION_SIDECAR_URL", "http://127.0.0.1:9")
["http://vision:8100/rescore", "http://127.0.0.1:8000/", "https://evil.test/p/1"],
"http://127.0.0.1:5432/",
"http://169.254.169.254/latest/meta-data/",
clipped = clip_text("a\x00b\x1b[31mcd", MAX_NOTES)assert not any(character in clipped for character in "\x00\x1b")
ruff
python-multipart, asyncpg, alembic, pgvector, pydantic-settings, python-dotenv, email-validator, argon2-cffi
@fontsource-variable/big-shoulders-display, @fontsource-variable/ibm-plex-sans, @fontsource/ibm-plex-mono, @radix-ui/react-collapsible, @radix-ui/react-dialog, @radix-ui/react-dropdown-menu, @radix-ui
ruff
* **frontend:** send a visitor whose session expired to sign in ([c8ec889](https://github.com/oldmoldycake/Snagr/commit/c8ec889b839fc31a7b8c97749290ffd7fe5fd2bf))
* **frontend:** send a visitor whose session expired to sign in ([714b0a0](https://github.com/oldmoldycake/Snagr/commit/714b0a065def5f75a3a9dbf3d103b13b46e9c7a7))
Gates applied: no_behavioural_pass.
3afec73517f6full audit observations/trust-audit/mcp-server/oldmoldycake__snagr.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-11 | 3afec73517f6 | SAFE | B | 86 | first audit |
Questions
What is the Snagr MCP server?
Selfhosted price tracker that searches marketplaces. A agent uses a headless browser to find, as well as recheck listings with a fastAPI backend and a live updating react dashboard
What tools does Snagr expose?
33 in total: 21 read-only, 7 that write, and 5 that can delete or overwrite (delete_category, delete_item, delete_site, discard_review_entry, revoke_reference). Every one is listed on this page with its risk.
Is Snagr safe to connect to an agent?
The audit found nothing in the source that contradicts what it says it does, and graded it B (86/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean server reads B. Separately from the audit: 5 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does Snagr need?
It reads AI_API_KEY, BRAVE_API_KEY, HF_TOKEN, JWT_SECRET, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, S3_ACCESS_KEY and S3_SECRET_KEY from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does Snagr run?
It speaks streamable-http, so it runs as a service you connect to over the network. It is published on npm as frontend at 0.0.0.
How current is this page?
The grade is for one exact copy of the source (3afec73517f6), read on 2026-10-11. The repository is watched and re-audited when it changes.