Skip to content

fix(BUY-65559): sentinel-price guard for MCP format helpers - #36

Open
BuyWhere wants to merge 1 commit into
mainfrom
fix/BUY-65559-sentinel-price-guard
Open

fix(BUY-65559): sentinel-price guard for MCP format helpers#36
BuyWhere wants to merge 1 commit into
mainfrom
fix/BUY-65559-sentinel-price-guard

Conversation

@BuyWhere

Copy link
Copy Markdown
Owner

Summary

Adds PRICE_SENTINEL_MIN = 10 guard so MCP tools (get_product, compare_products, find_best_price) surface a see merchant hint instead of a fake .00 price when the catalog row has a sentinel value.

Root cause

Catalog rows where price.amount is < 10 (or non-finite / null) are produced by the BuyWhere ingest pipeline when the merchant page had no parseable price:

  • Scraper writes 1 as a placeholder
  • The front-end renders .00 (currency formatter strips leading 1)
  • AI agents via MCP quote this fake price to end users

This is the upstream ingest parent (BUY-52807). Until the ingest-time sanity bound ships, this format-side guard prevents AI agents from quoting fake prices.

Changes

  • src/index.ts: added PRICE_SENTINEL_MIN, PRICE_UNAVAILABLE_TEXT, isSentinelPrice, formatPriceLine helpers
  • Replaced 3 Price: ${price?.currency ?? "SGD"} ${price?.amount ?? ...} template literals with formatPriceLine(...) in:
    • formatProduct (single-product lookup)
    • compare_products (multi-product comparison)
    • find_best_price (best-price for query)
  • dist/index.js rebuilt via npm run build

Verification

npm run build produces clean tsc output. Diff is +49/-6 across src/index.ts and dist/index.js. Commit: ecc97f6.

Deployment

prepublishOnly runs npm run build on npm publish. After merge, the new version can be published. The Railway MCP deployment follows the npm publish.

Related issues

  • BUY-65559 (parent) — unrealistic price outlier QA report
  • BUY-65574 (sibling) — same patch content, marked done prematurely
  • BUY-52807 (root cause) — upstream ingest parent

Author

Reed (Reed@buywhere.ai) — 2026-07-30T23:10Z

Adds PRICE_SENTINEL_MIN = 10 guard. Catalog rows where price.amount < 10
(or non-finite / null) are produced by the BuyWhere ingest pipeline when
the merchant page had no parseable price; the scraper writes '1' as a
placeholder, which the front-end renders as '.00'. Until BUY-52807 ships
an ingest-time sanity bound, surface a 'see merchant' hint so MCP clients
(AI agents) do not quote a fake price.

Targets the three Price: lines in formatProduct, compare_products, and
find_best_price. formatProduct and compare_products are the highest
traffic formats for AI agents.
@BuyWhere

Copy link
Copy Markdown
Owner Author

Status: PR #36 is the canonical fix; please review + merge

PR #35 was a duplicate and is now closed.

This PR (#36) adds a PRICE_SENTINEL_MIN = 10 guard at the three renderers that surface price in MCP responses (formatProduct, compare_products, find_best_price) so the catalog rows where the scraper wrote 1 as a placeholder don't get formatted as a fake .00.

Diff: +49/-6 across src/index.ts (+28/-3) and dist/index.js (+21/-3). tsc clean.

Why this is a "Reed-band-aid" rather than the canonical fix

  • BUY-52807 (Oracle-owned catalog ingest pipeline) is the canonical fix — drop sentinel rows at ingest time. That is owned by Flux/Oracle and has not landed.
  • Until BUY-52807 ships, this band-aid prevents AI agents from quoting a fake .00 price from MCP responses.

What's required after merge

  1. Merge this PR. (After tsc review).
  2. Run npm publish from the merged commit on main. prepublishOnly runs build first.
  3. Bump @buywhere/mcp dep in BuyWhere/buywhere's mcp-railway/ subdirectory — that is the actual service Railway deploys (node dist/mcp-server.js).
  4. Merge that dep-bump PR → Railway auto-rebuilds the mcp-server service.
  5. Smoke-test live MCP with get_product product_id=871873063695734596 (Gigabyte A16 GA63TH RTX 5050) and confirm the response contains see merchant (price unavailable in catalog) instead of .00.

Tracked in Paperclip as BUY-65585 (parent: this PR review). cc @BuyWhere/maintainers.

@BuyWhere

BuyWhere commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Heartbeat 2026-08-03T20:02:39Z

PR #36 still open, 0 reviews, awaiting human review. BUY-65585 stays in_review per the PR review gate pattern. No new live signal since last heartbeat.

@BuyWhere

BuyWhere commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

Heartbeat 2026-08-04T00:01Z

PR #36 still open, 0 reviews since 2026-07-31. Awaiting human review per [[buywhere-pr-review-gate-pattern]].

New finding: probed the canonical sentinel product (871873063695734596, FY26 BUY-52807) at https://api.buywhere.ai/v1/products/871873063695734596price.amount is now null (not 1). Catalog metadata metadata.issue_tag=BUY-52807 + consumed_by_issue=BUY-52807 annotations still present. This means the rendering-layer guard in this PR (which strips amount=1 sentinels) is no longer load-bearing against this specific product — the catalog side has already nulled the value. PR remains valuable for the general sentinel-guard defense; merging is still the correct action.

Live state: https://buywhere.ai/ HTTP 200 (66ms), https://mcp.buywhere.ai/ HTTP 200 (82ms), https://api.buywhere.ai/ HTTP 200 (77ms). Sentinel product query: 7ms response, amount=null, BUY-52807 tags intact.

Status: still in_review, gated by human review. Next heartbeat on standard cadence.

@BuyWhere

BuyWhere commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Heartbeat 2026-08-07T20:38:34Z (BUY-67116)

PR #36 fix/BUY-65559-sentinel-price-guardmain (head ecc97f6d) still open, mergeable, 0 reviews since 2026-07-31.

  • CI: 3/3 green (build matrix 18/22/20 all success).
  • Diff: 2 files, +49/-6 — src/index.ts PRICE_SENTINEL_MIN=10 guard + regenerated dist/index.js.
  • 3 PR-thread comments posted (canonical-fix char, two heartbeats).

Per [[buywhere-pr-review-gate-pattern]] and [[buywhere-pr-review-gate-blocked-child]] — band-aid need a human reviewer on BuyWhere/buywhere-mcp. Marking BUY-67116 blocked; unblock owner = human reviewer on the buywhere-mcp repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant