Scavenge

Scavenge

Enables deterministic web evidence collection by inspecting a field (price or availability) on a web page and returning structured observations from multiple channels (raw DOM, structured data, embedded state, rendered DOM, network JSON) with exact provenance, without interpreting correctness.

Category
访问服务器

README

Scavenge — deterministic web evidence for humans and coding agents

Scavenge inspects one field on one web page and reports every place that field appears, what each representation says, and exactly where each value came from.

It collects field observations with exact provenance. It does not determine which observation is semantically correct. That judgement needs to know what the page means, and the engine is deliberately incapable of it — see Why it stops there.

What it does

$ scavenge inspect https://example.com/product/123 --field price

FIELD: price
  RAW_DOM          99.00 USD   [raw_dom:0]
                     raw:    '$99.00'
                     source: div.product-price
  STRUCTURED_DATA  99.00 USD   [structured_data:0]
                     raw:    '99.00'
                     source: script[0]/offers/price
  EMBEDDED_STATE   not observed
  RENDERED_DOM     79.00 USD   [rendered_dom:0]
                     raw:    '$79.00'
                     source: div.product-price
  NETWORK_JSON     79.00 USD   [network_json:0]
                     raw:    '79.00'
                     source: GET https://example.com/api/products/123 /price

ACQUISITION
  HTTP    200  48213 bytes  0.31s
  RENDER  OK
  JSON responses observed: 12

The raw HTML says 99.00, the rendered page says 79.00, and an API call explains why. You can see that in two seconds; the engine does not assert it.

Why it exists

A page states the same fact in several places — visible HTML, JSON-LD, a hydration blob, the rendered DOM, an XHR response — and they disagree more often than you would like. Working out which ones carry your field is a DevTools job, done by hand, once per target, forever. An agent can do it too, but not reproducibly and not with exact provenance.

Why it stops there

The original goal was more ambitious: decide deterministically whether values found in different representations referred to the same real-world thing, and report where they agreed or disagreed.

Real storefronts broke that assumption. Values that looked comparable belonged to recommendation carousels, carts, financing tables, store locators, product variants, and second Product blocks on the same page. A validation run across 12 storefronts found that every disagreement the engine reported was of that kind — the values and provenance were right, the comparison was wrong.

Fixing it properly turns into entity resolution. So Scavenge stops one step earlier: collect the evidence deterministically and let a human or an agent interpret it. The full account, including the results that killed the earlier design, is in docs/research/.

Supported fields

price and availability. Two, not "arbitrary".

Supported channels

RAW_DOM · STRUCTURED_DATA · EMBEDDED_STATE · RENDERED_DOM · NETWORK_JSON

They are not equally validated. In a 23-page validation across 12 storefronts, STRUCTURED_DATA produced values in 28 of 46 runs and EMBEDDED_STATE in only 6.

MCP

One tool, inspect_web_field(url, field), returning structured evidence — not prose.

{
  "mcpServers": {
    "scavenge": { "command": "python", "args": ["-m", "scavenge.mcp"] }
  }
}

The server reasons about nothing, generates no scraping code, and calls no model.

Structured output

{
  "schema_version": 3,
  "target": "https://example.com/product/123",
  "field": "price",
  "observations": [
    {
      "id": "raw_dom:0",
      "channel": "RAW_DOM",
      "normalized_value": {"kind": "money", "amount": "99.00", "currency": "USD"},
      "raw": "$99.00",
      "provenance": {"selector": "div.product-price"},
      "subject": {"scope": "PAGE", "key": "", "reason": ""},
      "status": "OK",
      "note": ""
    }
  ],
  "acquisition": {
    "http_status": 200, "http_bytes": 48213, "http_challenge": "",
    "render_status": "OK", "json_responses": 12
  },
  "warnings": []
}

Architecture

  MCP ─┐
       ├─→ evidence engine ─→ HTTP · raw DOM · structured data
  CLI ─┘                      embedded state · rendered DOM · network JSON

One engine. Both interfaces call it; a test asserts the CLI's JSON is the engine's JSON.

Install

pip install git+https://github.com/aarohim24/Scavenge.git
python -m playwright install chromium     # required for rendered DOM and network channels
scavenge inspect https://example.com/p/1 --field price

Not on PyPI yet — deliberately, until the API has survived a round of external feedback.

--no-render skips the browser and reports the HTTP-only channels.

Security boundaries

http/https only. file://, localhost, loopback, link-local and private ranges are refused before any request — which matters more under MCP, where an agent supplies URLs without a human seeing them. Bodies capped at 512 KB, JSON responses capped at 40, bounded waits, bounded robots fetch. robots.txt is honoured, and a timeout or unreachable robots.txt is never treated as permission. No captured request is replayed, no authenticated request reissued, nothing from a page executed.

What it explicitly does not do

  • Decide which observation is correct. v0.1 publishes no comparison at all.
  • Entity resolution. It cannot reliably tell the page's product from a second Product block, an upsell tile, a cart total, or a store-locator row.
  • Crawl, schedule, or follow links.
  • Defeat anti-bot systems. It detects an obvious challenge and says so; it never bypasses one.
  • Call an LLM, in any mode.

Known limitations

  1. No comparison in v0.1. An earlier version published EQUAL/DIFFERENT relations. Real-world validation found that every DIFFERENT it produced on live storefronts compared two different entities, so the feature was removed rather than patched. The full result is in docs/research/OSS-FINAL-CORRECTNESS.md.
  2. Currency is often null. $ names a dozen currencies; without declared evidence the amount is kept and the currency refused. Unknown is common; wrong should be absent.
  3. Minor units are not modelled — a Shopify 2950 and a displayed 29.50 are two different observations.
  4. Determinism is of report generation, not of the web. Same bytes in, same report out. The same URL will not always give the same bytes.
  5. A blocked render is detected only from named signals; a thin unnamed shell is not.
  6. Robots-respecting access excludes many large retailers entirely.

License

Apache-2.0.

Status

Experimental v0.1. Published for technical feedback, not for production. The API may change. It has been validated on 23 pages across 12 storefronts, plus a deterministic fixture suite; that is a small sample and it is not a benchmark.

The research record — including the results that killed earlier versions of this idea — is in docs/research/.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选