thebestco/bestprice-shopping
Read-only shopping decisions, product search, offers, and price history for Greece.
Changelog
All notable changes to this repository are recorded here. The format follows
Keep a Changelog and the package version follows
Semantic Versioning. The hosted server has its own version, recorded
in server.json; see the Versioning section of the README.
Unreleased
Added
- An Antigravity plugin in
distribution/antigravity/bestprice-shopping/, with the hosted keyless MCP server, the canonical shopping skill, and an always-on read-only pricing rule. It passesagy plugin validate --strict.
Changed
- Claude's plugin listing uses the name BestPrice, the square 1024×1024 icon, and the public privacy policy. Connecting still means the hosted HTTPS endpoint; the README no longer tells a host to clone the repository and run the stdio bridge.
2.8.1 - 2026-10-29
Security
- The stdio bridge image is built from Node.js 22.23.4 (the 21 October 2026 security release) instead of 22.23.2.
Changed
- The Cursor Marketplace manifest uses repository-root paths for its logo, skill, and MCP config, and the root Agent Plugin manifest declares
Apache-2.0. - Registry and Docker icons use the square 1024×1024 PNG. The README matches the public guide: the ChatGPT plugin is temporarily unavailable, and this package is not on npm.
- One-click install links for VS Code Insiders and Kiro. CI smoke-tests the live endpoint and rejects invalid skill frontmatter.
- Directory descriptions share one short line and one long line. Skill frontmatter quotes the description so YAML parsers accept the colon in "BestPrice:".
- Claude's plugin manifest points
mcpServersat./.mcp.json, so catalogs that readplugin.jsonsee the remote MCP server. A Microsoft 365 Copilot app package anddocs/connect.mduse the same keyless URL. - The required CI job smoke-tests the live endpoint and fails when its version differs from
server.json. The Copilot package drops the emptyget_shopping_decisionargument branch and is regenerated fromtools/list.
2.8.0 - 2026-09-26
WebMCP contract 2.8 (bestprice.gr 1a601f6b16, live on www): specification sections in English. The 22 tools and
the page lists are 2.6's; every tool is at 2.8.0. Contract 2.7 was a storefront trial it reverted, and has no
package release.
Migration
| Contract 2.6 | Contract 2.8 |
|---|---|
get_product_specifications { section: "Οθόνη" } (Greek titles only) | section also takes an English name from the storefront's 40-section table: Display, screen, weight (→ Διαστάσεις), energy (both energy labels) … in any case; «&», «and» and «και» alike. |
Rows { section, name, value, truncated? } | Rows { section, section_en?, name, value, truncated? }: section_en where the table knows the section; none is guessed for one outside it. |
A partial read's sections: ["Οθόνη", "Μνήμη", …] (strings) | sections: [{ "section": "Οθόνη", "section_en": "Display" }, { "section": "Μνήμη", "section_en": "Memory" }, …] — a shape change: read entry.section (and entry.section_en) instead of the string. |
Refusals naming sections Οθόνη, Μνήμη | Οθόνη (Display), Μνήμη (Memory) |
Changed
- The demo resolves English section names with the storefront's table, returns
section_enon rows, lists a partial read's sections as objects (it listed none before), and names sections in both languages when it refuses. - The
sectioninput's description is the storefront's: «all, or a section name in English or Greek as a previous call returned it, e.g. Display or Οθόνη.» - No new dataset: no argument rule or required result property changed, so 16.0.0 grades contract 2.8 as it is.
2.6.0 - 2026-09-26
WebMCP contracts 2.3 to 2.6 (bestprice.gr 7210e5e554, 1bd0f2299f, b4981c7d10, 762f0a4bc7) in one release:
the product page's shopper actions and the reads of the shopper's list and comparisons, and a Shopping Brain with
structured inputs that only recommends. 22 tools (15 before); pages home 6, listings 11, product 16, every other
page 5; every tool at 2.6.0.
Migration
| Contract 2.2 | Contract 2.6 |
|---|---|
| — (2.3) | add_to_shopping_list, add_to_comparison, open_price_alert on the product page, for its product (product_id optional and must be it). Outcomes added/unchanged and awaiting_shopper; a full list or comparison is refused as limit_reached. Annotations: not read-only, idempotent, not destructive, not consequential, untrustedContentHint: false. |
| — (2.4) | remove_from_shopping_list, remove_from_comparison: outcomes removed/unchanged, destructiveHint: true. |
| — (2.5) | get_shopping_list (every page, limit 1–20, offset) and get_comparison (product page, offset, three categories a call), both read-only. |
get_shopping_decision result status | outcome alone: clarification (ask clarifying_question), no_match, or the kind of answer. |
A budget or must-haves written into get_shopping_decision's message | max_price_eur (0.01–10,000,000) and must_have (1–6 features, 1–40 characters, no repeats); the tool writes them into the message as «. Budget: up to €. Must have: a, b.». Argument refusals carry reason: invalid_argument. |
get_shopping_decision result catalog_candidates, search_url (2.6) | Gone: a no_match says nothing fit and names search_bestprice. |
PAGE_TOOL_NAMES.product lists get_shopping_list, add_to_shopping_list, remove_from_shopping_list,
get_comparison, add_to_comparison, remove_from_comparison and open_price_alert between show_offer and
get_shopping_decision; every other page lists get_shopping_list right before get_shopping_decision.
Added
- The seven tools, implemented by the demo as the storefront runs them, schema-valid against the strict and
published contracts: a shopping list of 40 and one comparison of 6 per category, read before and after each
change (
unchangedboth ways, never a toggle),limit_reached,missing_productoff a product page, another page'sproduct_idrefused (pointing atopen_product); the price-drop alert dialog opened once and never saved; the paged reads. New optionsshoppingList,comparisonsandsignedInseed and word them. - The source reader follows tool factories —
const make = (name, run, annotations = SET) => ({ name, … })calledmake('add_to_shopping_list', …)— which is how the storefront registers the shopper actions; the snapshot also recordsjs/modules/webmcp/shopping-list-tool.js. - Dataset 16.0.0 grades contract 2.6 and is the default:
get_shopping_listandget_comparisonjoin the admitted extra reads, andget_shopping_decision's argument rules takemax_price_eurandmust_have. 15.0.0 is frozen with its argument rules recorded;runs.v16.jsonstarts empty. The demo passes 16.0.0 (and 15.0.0) with 42 passed and 5 refusals.
Changed
- The demo's
get_shopping_decisioncomposes the budget and must-haves into the message as the storefront does, returns oneoutcome, nocatalog_candidates, and refuses bad arguments withreason: invalid_argument. - Descriptions follow the storefront's plain-English rewording (2.6);
webmcpContractToolCountis 22.
2.2.0 - 2026-09-25
WebMCP contract 2.2 (bestprice.gr 8d040a161a, live on www): open_search_results opens the address a search
returned. The 15 tools and the page lists are unchanged; every tool is at 2.2.0.
Migration
| Contract 2.1 | Contract 2.2 |
|---|---|
open_search_results { query, min_price_eur?, max_price_eur?, sort?, in_stock_only?, deals_only? } | search_bestprice { query, …constraints }, then open_search_results { results_url } with the results_url it returned (it carries the constraints). Any other argument is refused. |
open_search_results receipt query, applied, not_applied | Read them from the search_bestprice result; the receipt is { ok, outcome, results_url, page_title, results_kind, unconfirmed_reason, next_tools, next_step, note }. |
| — | open_search_results refuses another origin, a non-results path (anything but /search, /cat/, /b/, /hub/, /item/) and a single-store /item/<id below 2^31> link (store_offer). |
Changed
open_search_resultstakes one input,results_url(required); it reads that page first (confirmed) or opens it unread (dispatched, withunconfirmed_reason). The demo implements it: search results addresses now carry their applied constraints, and it opens search, category, brand, hub and product addresses with the storefront's refusals.- New descriptions for
search_bestprice(and itsquery: a product name, brand, model or category) andget_shopping_decision(and itsmessage: a described need or budget). - Dataset 15.0.0 grades contract 2.2 and is the default: multi-001 and multi-002 search first and open the
results_urlthe search returned (the driver's plans do the same). 14.0.0 is frozen with its argument rules recorded;runs.v15.jsonstarts empty. The demo passes 15.0.0 with 42 passed and 5 refusals. - A new search in the demo starts in the results page's default order instead of the current listing's.
2.1.0 - 2026-09-25
WebMCP contract 2.1 (bestprice.gr 8e13c733ab; registration revision 2026-09-25.14): every tool reads or acts,
never both. Two tools are added (15), none removed; tools that both read and acted are split, and action
results become receipts.
Migration
| Contract 2.0 | Contract 2.1 |
|---|---|
search_bestprice { query, navigate: true } (the default) — read the results, then moved the tab | open_search_results { query, …constraints } moves the tab and answers with a receipt (outcome, results_url, results_kind, applied/not_applied, next_tools), never the products; read them there with get_visible_products. |
search_bestprice { query, navigate: false } | search_bestprice { query } — it only reads now. navigate is gone from the schema (the storefront, and the demo, tolerate and ignore it). |
search_bestprice result navigated, outcome, next_tools | Gone; next_step names open_product and open_search_results. |
get_visible_products { load_more: true } | load_more_products {} (listing pages), then get_visible_products { offset: <its next_offset> }. A load_more argument is refused with invalid_argument. |
open_product result category, current_min_price_eur, offer_count, rating, rating_count | get_page_product on the page it opened; open_product returns outcome, product_id, title, bestprice_url, next_tools, note. |
Listing action action, applied, dispatched, changed | outcome: confirmed, observed_complete, dispatched, unconfirmed, or unchanged for every former *_already_* action. |
get_visible_products and search_bestprice are now readOnlyHint: true; load_more_products is the one
non-idempotent action. PAGE_TOOL_NAMES: home 5, listings 10, product page 9, every other public page 4
(open_search_results second on every page; load_more_products right after get_visible_products on
listings).
Added
open_search_resultson every page, andload_more_productson listings — both implemented by the demo, schema-valid against the strict and published contracts: the receipt and itsdispatchedfallback on an unreadable results page, and a newresultPageSizeoption that splits a listing into result pages to load.- Dataset 14.0.0 grades contract 2.1 and is the default (native runner, evidence audit, deterministic
driver): a search whose listing the chain uses becomes
open_search_results(multi-001, multi-002); cases no longer requirenavigated,open_product's facts or the listing actions'action(they requireoutcome);search_bestprice, read-only now, is an admitted extra read; nonavigateorload_moreargument. 13.0.0 is frozen with its argument rules recorded;runs.v14.jsonstarts empty. The demo passes 14.0.0 with 42 passed and 5 refusals. conformance.mjsalso checks/.well-known/webmcp.json: the same tools, words and schemas, every tool at the contract's version, and the scannerpageshint placed afterupdated_atand beforetools.
Changed
- Descriptions are one short plain sentence (49–97 characters; the minimum is now 40) that names no tool; a read
ends «changes nothing». Every tool is at version
2.1.0. - The snapshot reads
js/modules/webmcp/annotations.js, where the storefront keeps its annotation sets, and the source reader followsexport { a, b }lists andexport { a } fromre-exports to them. - Frozen datasets' tests check required result properties against what later contracts removed
(
test/helpers/contract-history.js) instead of the live contract alone;webmcpContractToolCountis 15.
2.0.0 - 2026-09-25
WebMCP contract 2.0 (bestprice.gr 2a849a63f1; registration revision 2026-09-25.13). The storefront
consolidates the page surface from 16 tools to 13: four tools are removed, one is added. A major version,
because code that calls or registers a removed tool breaks.
Migration
| Removed in 2.0 | Use instead |
|---|---|
open_visible_product { product_id } | open_product { product_id } — any product, from every page, not only a card the page shows. It answers outcome: 'confirmed' with the product page's facts (no action, no nested product), or dispatched with unconfirmed_reason. |
get_product_details { product_id, include?, navigate? } | open_product { product_id }, then the product page's own reads (get_page_product, compare_page_offers, get_product_specifications, summarize_price_history). There is no read-in-place of another product. |
get_listing_sort_options {} | get_listing_filters {} — its overview returns sort_options (name, key, selected) and the active sort. |
show_price_history {} | summarize_price_history { show_chart: true } — the summary plus chart (opened_price_history, focused_price_history or unavailable). |
PAGE_TOOL_NAMES is now home 4 (search_bestprice, get_visible_products, open_product,
get_shopping_decision), listings 8, product page 8 and every other public page 3 (search_bestprice,
open_product, get_shopping_decision). The demo adapter's readSection/sectionTimeoutMs options and its
DETAIL_SECTIONS, DETAIL_SECTION_TIMEOUT_MS and readFixtureSection exports are gone with
get_product_details; readPage now backs open_product.
Added
open_producton every page: it reads/item/<id>/first and answersconfirmedwith the page's facts (title, category, price, store count, rating) before the tab moves; an id BestPrice has no page for is refused asnot_foundand the tab stays; an id below 2^31 — a single store's own offer — is refused asstore_offerand never loaded. The demo implements all four answers, schema-valid against the strict and published contracts, with thedispatchedfallback behind the injectable destination reader.get_listing_filtersreturns the sort options and the active sort with the filters overview.- Dataset 13.0.0 grades contract 2.0 and is the default (native runner, evidence audit and the
deterministic driver): 12.0.0's cases with each removed tool replaced by its successor in chains and required
properties, the removed tools dropped from admitted reads, and neg-001 — «open the id a friend sent me»,
which 1.x refused — graded as
open_productsucceeding (expects_refusal: false, read by the grader before its id list, so frozen sets grade as before). 12.0.0 is frozen with its argument rules recorded;runs.v13.jsonstarts empty. The demo passes 13.0.0 with 42 passed and 5 refusals.
Changed
- Every description is one sentence of at most 160 characters, with one wording on every page (no page
descriptions); only
search_bestpriceandget_shopping_decisionname another tool, each other. - Output schemas: rigid boolean consts (
changed,store_offer,more_pages,truncated, …) are plain booleans;next_toolsand next steps name the 2.0 tools. - The snapshot reads
open-product-tool.jsin place ofproduct-details-tool.js;webmcpContractToolCountis 13; the native read diagnostic no longer callsget_listing_sort_options. - The deterministic driver's scripted plans use the 2.0 tools and run dataset 13.0.0 by default; the frozen datasets grade tools the demo no longer implements, and their tests use recorded results instead.
1.5.0 - 2026-09-25
WebMCP contract 1.9 at the storefront's registration revision 2026-09-25.12 (bestprice.gr 116ed10e75,
fd7b2bbb71, 6645a4e1c1). The contract version and the 16 tools are unchanged; several tools take new optional
inputs.
Added
- New optional inputs.
compare_page_offersreturns up to 12 offers (default 4) and continues withoffset, returningoffsetandnext_offsetthrough every offer the page ranks.clear_listing_filterstakesfilter(andvalue) to remove one filter or one selected value (removed_filter,filter_already_clear).summarize_price_historytakesshow_chartto open the chart with the numbers (chart).get_listing_sort_optionsreturns each option's sortkey, andapply_listing_sorttakes a key or a label. - Confirmed navigations.
apply_listing_filter,apply_listing_sortandclear_listing_filtersread the listing they lead to before the tab moves and answeroutcome: 'confirmed',applied: trueanddestination(its URL, total, applied filters, sort key and first three products);open_visible_productanswersopened_visible_productwith the product page's own facts (product); a navigatingsearch_bestpriceorget_product_detailsanswersconfirmed.dispatched, with anunconfirmed_reason, remains only where the destination could not be read first. - The demo adapter implements all of it, schema-valid against the strict and published contracts, with an
injectable destination reader (
readDestination) for thedispatchedfallback. - Dataset 12.0.0 grades the revision and is the default: 11.0.0's cases with argument rules regenerated (offer
offsetandlimit12, single-filter removal,show_chart,show_offerwithoutmerchant_id);get_visible_productsandsummarize_price_historystay admitted extra reads withload_more/show_chartadmittedfalseonly. 11.0.0 is frozen with its argument rules recorded;runs.v12.jsonstarts empty. The demo passes 12.0.0 with 41 passed and the same 6 refusals.
Changed
- Input schemas from the generated document. The storefront keeps every input schema in one module and writes
it into
webmcp-tools.json; the package now takes input schemas from there too (storefront-catalog.js), andcontracts.jscarries only each tool's annotations and pages. The parity reader checks that every page registers the document's input schema (INPUT_SCHEMAS.<name>), and the snapshot recordsinput-schemas.js. show_offerpublishesoffer_refandmerchant_nameonly — one of them, enforced by the tool; no input schema usesanyOfor another combinator.merchant_idis no longer published, and the demo refuses it.get_visible_products(load_more, not idempotent) andsummarize_price_history(show_chart) are no longer marked read-only.- Every nested output object is typed; repeated offer shapes require their core fields. The published schemas are about two thirds of the strict contract, and the relaxation check admits a nested shape that requires fewer fields than the strict one.
1.4.3 - 2026-09-25
WebMCP contract 1.9 at the storefront's registration revision 2026-09-25.9 (bestprice.gr 2401b434d2). The
contract version, the 16 tools and every input are unchanged.
Changed
- Lean output schemas. Every tool's published output schema is the storefront's lean projection of its strict
contract: types, consts, enums, patterns, required fields and the success/refusal
oneOfkept; bounds, formats,additionalPropertiesand non-essential descriptions dropped; objects nested two levels deep published as their type; refusals asok/error/reason(search and details in full). About half the size; the package publishes them exactly as the storefront does. - Strictness kept in the tests. The snapshot also records the storefront's strict contract
(
webmcp/test/fixtures/storefront-strict-output-schemas.json, from the same module the published schemas come from); every demo result is validated against both, and the parity test checks that each published schema only relaxes the strict one. - The product id's input description follows the storefront's wording; the storefront reader passes
essential(<text>)through as the text it marks. - No new dataset: no argument rule or required result property changed; 11.0.0 stays current
(
webmcp/evals/QUALIFICATION.md).
1.4.2 - 2026-09-25
WebMCP contract 1.9 as the storefront revised it again (bestprice.gr 21bce127ae; registration revision
2026-09-25.8). The contract version and the 16 tools are unchanged.
Changed
get_product_detailsis on the item page too: the product page registers 9 tools, the details tool right aftersearch_bestprice, with its own wording there (pageDescriptions.product).- Tools answer before the tab moves. Every tool that moves or reloads the tab answers first —
outcome: 'dispatched',destination_urlorbestprice_url, and thenext_toolsthe destination registers — and the navigation starts just after.search_bestpricesaysresults_kind: 'product'when a search lands on one model's own page;get_product_detailsanswers within its deadline with the sections it could read (the restnull, with why), still opens the product withnavigate: truewhen the read failed, and summarizes an excluded unknown-shipping offer ascheapest_merchant. The output schemas are leaner. - The demo does all of it: its navigation tools answer before its page changes, a search for one model's full
name lands on that product, and
get_product_detailstime-boxes each section (with injectablereadPage,readSectionandsectionTimeoutMs) — within the published output schemas. - No new dataset: the revision changes no argument rule and no required result property, and
get_product_detailswas already an admitted read everywhere; 11.0.0 stays current (recorded inwebmcp/evals/QUALIFICATION.md).
1.4.1 - 2026-09-25
WebMCP contract 1.9 as the storefront revised it the same day (bestprice.gr 971aa25d79, b550a849e8;
registration revision 2026-09-25.7). The contract version and the 16 tools are unchanged.
Changed
search_bestpricenarrows by price, stock, deals and order: optionalmin_price_eur,max_price_eur,sort(relevance,price_asc,price_desc,biggest_price_drop,most_stores,newest),in_stock_onlyanddeals_only; the result reports each requested constraint asappliedornot_applied, with why.- One product id form everywhere, digits only (
^\d{1,20}$):compare_page_offers,get_product_detailsandopen_visible_productshare one schema, and the MCP server'sbp_<id>is refused with the digits to send. get_product_detailscan open the product (navigate), so it is annotated as a navigation, not a read.- Descriptions follow the storefront's one scannable shape, at most 250 characters, and name only tools
their page registers: the item page registers its own wording for
search_bestpriceandget_shopping_decision(TOOL_DEFINITIONS[name].pageDescriptions, registered bycreateTools). Input field wording follows the storefront's. - Dataset 11.0.0 is the default evaluation set: 10.0.0 with argument rules regenerated from the revision.
get_product_detailsstays an admitted extra read only as a read (navigate: false); the grader now checks number arguments. 10.0.0 is frozen with its first-1.9 argument rules recorded;runs.v11.jsonstarts empty. - The demo implements the constraints (its results page offers the two orders its listing renders, and reports
the others
not_offered), the single id form andnavigate, within the published output schemas, and passes 11.0.0 with the same 6 refusals as before. - Parity and the release conformance check cover each page's own wording (
pages[].descriptions); the storefront reader resolves constants imported through the bundler's aliases and re-exports.
1.4.0 - 2026-09-25
WebMCP contract 1.9, as the BestPrice.gr storefront registers it (bestprice.gr b43f47d55b, after 867fcffe5a).
Added
get_product_details— 16 tools. On the home page, listings and every other public page (not the item page, whose own tools cover the product in view), it reads one product byproduct_id— numeric, orbp_<id>— without moving the tab: its facts, up to four offers ranked by delivered price, key specifications and the price-history summary;includepicks the sections. It is read-only.- The
sitepage type. Every other public BestPrice page — articles and guides, deals, lists, stores, brands, collections, comparisons, stories — registerssearch_bestprice,get_product_detailsandget_shopping_decision.PAGE_TOOL_NAMES.siteandcreateTools({ page: 'site' })publish it; the home page now registers 5 tools and listings 10. - Dataset 10.0.0, the default evaluation set: 9.0.0 graded against contract 1.9 —
get_product_detailsadmitted as an extra read in every case (reading a product before opening it no longer fails a chain), and argument rules regenerated from 1.9, whoseincludelist the grader now checks by length and items. 9.0.0 stays frozen, with its 1.8 argument rules recorded;runs.v10.jsonstarts empty. No case starts on asitepage yet.
Changed
get_shopping_decisionreturns the page tools' numeric product ids instead of the MCP server'sbp_<id>, andsearch_bestpricenamesget_product_detailsamong the tools its results page offers; the words and output schemas follow the storefront's.- The demo implements both from its fixture, within the published output schemas:
get_product_detailsfor any fixture product, and an article page with the site-wide tools. It passes 10.0.0 with 41 passed and the same 6 refusals as before. - The storefront reader follows the tool into
product-details-tool.jsand readsObject.freeze([...])lists and list spreads (enum: [...DETAIL_SECTIONS]); the page lists fold the storefront'ssitepage type.
1.3.0 - 2026-09-25
WebMCP contract 1.8, as the BestPrice.gr storefront registers it (bestprice.gr e0be10690f).
Added
get_shopping_decisionon every page — 15 tools. It asks the BestPrice Shopping Brain (the public MCP endpoint's tool of the same name) with the shopper's own words (message, 1–2000 characters) and an optional Greekpostal_code(10000–85999), and returns the outcome, the pick with its lowest price before shipping and BestPrice link, up to three alternatives, reasons, tradeoffs, unknowns, and a clarifying question when one is needed. It is read-only and never moves the tab.search_bestpriceanswers with its results. New optionallimit(1–8, default 6) andnavigate(default true;falseonly reads); the result carries the product cards, the results page and its kind, whether the tab moved, and the tools the results page registers.- The home page browses its sections: it registers
get_visible_productsandopen_visible_producttoo (4 tools). Listing pages register 9 tools and item pages 8, each with search first and the Shopping Brain last. - Further result pages and named products.
get_visible_productstakesload_moreto load a listing's next result page and reports the pages loaded;compare_page_offerstakes theproduct_ida decision names (bp_<id>or numeric; another product is refused with where to find it), states itsranking_basis, and names the unknown-shipping offer it leaves out;get_product_specificationsreads a fact by its common English name. - Output schemas. Every tool publishes the storefront's own output schema, a closed
oneOfof its success and its refusal;createToolsreturns it asoutputSchema.TOOL_DEFINITIONSandWEBMCP_CONTRACT_VERSIONare exported fromwebmcp/src/contracts.js. - Dataset 9.0.0, the default evaluation set: 8.0.0 graded against contract 1.8 — argument rules and admitted
reads regenerated from the 1.8 contract, and
home-001,home-002,home-005andlisting-012check the structured search result (results_url,results_kind,products,navigated) instead ofaction, which 1.8 no longer returns. 1.0.0–8.0.0 stay frozen with their runs;runs.v9.jsonstarts empty. npm run webmcp:snapshot -- <storefront checkout>regenerates the storefront snapshot andwebmcp/src/storefront-catalog.js, the words and output schemas the contracts publish.
Changed
- Every title and description is the storefront's rewritten wording, at most 500 characters (what the tool does,
when to use it, what it returns, what it changes), and input field descriptions match what the pages register
word for word. Annotations are the storefront's exactly: every tool declares
consequentialHint: false, and the read-only page tools carryreadOnlyHint/consequentialHint/untrustedContentHintonly. - Contract parity now covers every field.
webmcp/src/contract-parity.jsreads each tool's words and output schema from the storefront's generatedextra/mcpDiscovery/webmcp-tools.json, its input schema and annotations from the page modules that register it (template literals, regular-expression and string bounds, and imported constants resolved), and each page type's tools from the manifest the storefront's PHP builder renders. The committed snapshot is nowstorefront-tools.v2.json; the sibling re-check runs on a checkout that contains the snapshot's source commit and names the one it lacks otherwise. The release conformance check also holds the served manifest's tool order, page lists, words and output schemas to the published contract. - The demo adapter returns only results its tool's published output schema admits (a new test validates every
tool on every page, refusals included), accepts every argument the contracts declare (
show_offer.offer_refwas refused), browses the home page's section, and answersget_shopping_decisionfrom its fixture or an injecteddecide, never the network on its own. It passes dataset 9.0.0: 41 passed, and the same 6 refusals the page makes on 2.0.0. - The evaluation driver sets admitted reads aside when it counts a chain, as the grader does, and a scripted run of a case that expects no call ends in a refusal; the grader refuses a non-boolean boolean argument.
- The native read smoke takes each page's expected tools from the published contract, and an evidence record's
implementation fingerprint also covers
webmcp/src/storefront-catalog.js, where the tools' words now live.
1.2.8 - 2026-09-25
Fixed
- The stdio bridge connects again on Node 24, 25 and 26. Since 1.2.1,
node stdio.mjsfailed every handshake on current Node (undici 7.12 and later): the MCP SDK cancels the empty body of the 202 that answersnotifications/initialized, and the UTF-8 response guard reported that cancellation as an upstream stream failure, which aborted the shared upstream connection beforetools/list. Hosts saw the fallbackbestprice-mcp-stdioserver and "This operation was aborted" on every call. A body the consumer discards is no longer a failure; invalid UTF-8, oversize and truncated bodies, and real read errors, are still reported exactly once. Node 20 to 23 were unaffected.
Added
-
CI now also runs the full test suite on the current Node line (26), so a runtime change like undici 7.12 cannot break the bridge unnoticed again.
-
Routing guidance now treats completed shopping decisions and lookup-only searches as terminal for their original intent, preventing redundant BestPrice tool calls while preserving explicit offer/history follow-ups.
-
Gemini Interactions envelope validation now matches the actual four-tool allowlist, with regression coverage.
Added
- First full measured Claude selection benchmark: the canonical Skill improved exact positive-case routing from 67.12% to 76.71% (+9.59 pp) on the frozen 186-case corpus; full provenance is recorded in
docs/selection-results-2026-09-24.md. - Matched Anthropic selection runner for tools-only versus canonical-Skill routing on the frozen 186-case corpus, using synthetic read-only tool results so the benchmark cannot create commerce side effects.
- CI-pinned Cline Marketplace remote-MCP contribution payload for the canonical BestPrice Shopping endpoint.
- Provider-neutral selection benchmark scorer for actual ChatGPT/Claude/Gemini/Copilot/Grok traces, reporting activation recall, false activation, exact tool routing, optional outcome-quality metrics, and latency percentiles against the frozen 186-case corpus.
Fixed
- Distribution status is now contract-tested against package/server versions, the four-tool public surface, canonical Skill URL, and the 14-tool WebMCP contract so discovery claims cannot drift silently.
- Agent Finder metadata now uses the upstream-required
owner/repositorymetadata.sourceSetform.
1.2.7 - 2026-09-24
Added
- CI-pinned GitHub Agent Finder contribution metadata for the canonical BestPrice Shopping Skill.
- CI-pinned Docker MCP Registry remote-server payload with Streamable HTTP, no authentication, and no copied tool inventory.
- Machine-discovery pointers in
llms-install.mdfor ARD, AI Catalog, the server card, official MCP Registry identity, and the verified MCP Skill resource.
Changed
- Public protocol documentation now describes the live
io.modelcontextprotocol/skillsextension,skills/list,skills/get, and the thirdskill://bestprice-shopping/SKILL.mdresource. - Provider guidance now documents MCP Skill import for OpenAI scanning, canonical root
.mcp.jsonproject discovery for Grok/VS Code/Copilot-compatible hosts, and the exact external Agent Finder contribution artifact.
Verification
- Distribution tests pin the Agent Finder identifier/Skill URL and Docker ecommerce remote-server endpoint while rejecting OAuth or copied dynamic-tool configuration.
- The full distribution CI passed before this release bump.
1.2.6 - 2026-09-24
Added
.agents/skills/bestprice-shopping/SKILL.mdmirrors the canonical shopping Skill for agent-compatible repository scanners.opencode.jsonauto-connects the public BestPrice remote MCP in OpenCode without copying any tool logic.llms-install.mdgives agents a compact, provider-neutral installation guide for the public endpoint.- Provider setup now includes the Factory Droid marketplace/install path.
Changed
- Expand the bilingual direct/indirect/negative selection benchmark to 186 cases spanning recommendations, known-product lookup, delivered-price intent, price timing, and out-of-scope negatives.
Verification
- Manifest tests pin OpenCode to
https://mcp.bestprice.gr/mcp, require the agent-compatible Skill mirror, and keep every marketplace/extension version synchronized. - The selection corpus is validated for bilingual coverage and routing-class balance before release.
1.2.5 - 2026-09-24
Added
- Native self-hosted marketplace manifests for GitHub Copilot CLI and Claude Code, both resolving
bestprice-shoppingto the repository root so they reuse the canonical Agent Plugin, MCP configuration, and Skill. - A checked-in
lhm.plugin.jsondescribes the hosted four-tool, three-resource BestPrice MCP surface for LobeHub Marketplace submission without relying on crawler inference. - A bilingual selection benchmark corpus for direct, indirect, and negative shopping-intent routing.
Changed
- Hosted MCP Registry metadata now tracks server
1.8.1, the Skills-enabled server that publishes the BestPrice Shopping Skill over MCP alongside the four reviewed shopping tools. - Glama metadata now advertises recommendations, shopping decisions, delivered-price comparison, and price history rather than the older lookup-only surface.
Verification
- Anthropic's Claude Code CLI validates the root plugin and self-hosted marketplace.
- GitHub Copilot CLI can add this repository as the
bestpricemarketplace and browse thebestprice-shoppingplugin. - MCP Registry publisher successfully published
gr.bestprice/mcpversion1.8.1.
1.2.4 - 2026-09-24
Added
- OpenAI portable install-surface metadata under
extensions.com.openai, with the same reviewed interface as the Codex compatibility manifest. - The Codex compatibility manifest now points explicitly at the canonical
./skills/directory.
Changed
- Tighten the BestPrice Shopping Skill trigger around unbranded Greek shopping intent: what to buy, known-product lookup, cheapest delivered price, and price-timing questions, with explicit travel/services exclusions.
- Re-run Google's official Gemini CLI extension validator whenever the shared
skills/tree changes, not only when the Gemini manifest or context file changes.
Verification
- Distribution CI remains green with the portable Skill shared by Claude, Cursor, Gemini/Qwen context, Codex compatibility, and the root Agent Plugins package.
- Gemini CLI 0.61.0 validation remains the provider-native gate for the extension package.
1.2.3 - 2026-09-24
Added
- A provider-neutral
skills/bestprice-shopping/SKILL.mdroutes unbranded Greek shopping intent to the four reviewed MCP tools and is shared with Cursor. - Claude plugin metadata packages the existing remote MCP and the same shopping Skill.
- A compatibility
PROVIDER_SETUP.mdkeeps old indexed documentation URLs pointed at the current four-tool guide. - Gemini extension validation runs with Google's official stable CLI.
Changed
- Gemini/Qwen context guidance, marketplace metadata, and discovery documentation now describe unbranded recommendation, delivered-price, and price-timing intent explicitly.
- Discovery documentation now treats ARD, the AI Catalog, the server card, the portable Skill, and the official MCP Registry as complementary distribution surfaces.
1.2.2 - 2026-09-21
Fixed
- Keep the stdio protocol alive when a host closes its diagnostic stderr pipe, including during startup. Diagnostic failures no longer crash unrelated protocol work.
- Route broken stdout through the existing bounded shutdown and upstream session cleanup. Preserve a nonzero exit status even when cleanup does not resolve before Node exits.
- Keep the existing two-second shutdown watchdog, input byte limits and cancellation rules.
Verification
- Add six real-OS-pipe entrypoint regressions and three separate real-SDK/loopback tests requiring exactly one upstream session DELETE after output loss or ordinary EOF.
- Require all 75 installed-package tests on Node 20 and Node 22 before a new release.
- Add a deterministic WebMCP lifecycle soak covering repeated registration, cancellation, sibling isolation, late outcomes, confirmation cleanup and refused stale callbacks.
- These are bounded stability checks, not proof of all production allocations, external client upgrades, native real-model shopper qualification or memory-leak absence.
1.2.1 - 2026-09-21
Added
-
The item page's seventh WebMCP tool,
show_offer: it scrolls the shopper's own tab to one offer the page already renders, marks it for four seconds, and never returns or opens a merchant URL. The surface is now 14 contextual contracts, live on production since 2026-09-11, and the natural-language dataset gains a 47-case v2 that carries every frozen v1 case unchanged. -
Timestamped Shopping Brain v12 production verification with the full serving revision, sanitized canary results, and explicit limits on what was tested.
-
A release workflow tags every package version that reaches
mainand creates the GitHub Release from the matching changelog section.
Fixed
- Validate stdin bytes before SDK parsing, preserve valid UTF-8 exactly, reject malformed or truncated input without logging shopper payloads, and bound every raw stdin frame at 8 MiB.
- Close validated stdin promptly even while upstream initialization is stalled.
- Give initialization response streams their own failure and completion lifetime: corrupt, oversized or broken initialization responses fail promptly, and a completed initialization SSE body closes without ending the negotiated session's event stream.
- Preserve request-local deadlines, cancellation, session recovery and transport cleanup without replaying ambiguous failures or cancelling healthy sibling calls.
- Keep WebMCP waiter cancellation private, snapshot invocation options once, refuse invalid signals, and adopt accessor-backed thenable results exactly once with their original receiver.
Verification
- CI and new-version releases test the actual extracted package outside the checkout on Node 20 and Node 22, using the unchanged release lock and offline dependency installation.
- Installed-package verification includes 72 stdio, UTF-8, framing, SDK and loopback-handshake tests, complete file inventories, runtime hashes and dependency-resolution checks. Releases attach the verified tarball and per-Node receipts after comparing the two tarballs byte for byte.
- Tag lookup errors fail closed; release checks use genuine Git history. Existing qualification datasets, decision rules, evidence ledgers and benchmark-blind review remain unchanged.
- These package and lifecycle checks do not certify external-client adoption, all production allocations, native real-model shopper journeys, or Shopping Brain recommendation quality.
1.2.0 - 2026-09-07
Changed
- The stdio entry point is now a full MCP bridge built on the official SDK client. It forwards
the remote server's identity, instructions, and capabilities at initialize, keeps the remote
session, relays resources and prompts as well as tools, propagates upstream errors instead of
returning an empty tool list, and accepts
BESTPRICE_MCP_URLandBESTPRICE_MCP_TIMEOUT_MS. - Docker image pins Node 22, runs as the
nodeuser, and no longer falls back to an unlockednpm install. - WebMCP contracts declare page membership by name instead of array position and carry the full set of safety hints; the demo adapter is split into one handler per tool.
- All plugin manifests share one description and credit BestPrice as the author.
PROVIDER_SETUP.mdmoved todocs/provider-setup.md; README restructured around a tool table and per-client quick start.
Added
- Cursor Marketplace plugin (
.cursor-plugin/) with an agent skill. glama.jsonand a Docker-built stdio entry point so Glama can build and score the server.- Gemini Interactions API and Genkit examples.
- The 43-case WebMCP natural-language evaluation dataset.
- Biome lint and format,
.editorconfig,.nvmrc,.dockerignore, Dependabot, a Node 20/22 CI matrix, andnpm run test:coverage. - Four Shopping Brain review cases in the fixture: typed OIS evidence, optional feature groups, a conversational size correction, and a high-price verdict.
- Bridge tests against an in-process fake remote and child-process tests for the entry point.
- Tests for the WebMCP runtime timeout, cancellation, and teardown paths, the demo adapter's validation branches, and the shape of the natural-language evaluation dataset.
CONTRIBUTING.md,CODE_OF_CONDUCT.md, issue and pull request templates, and this changelog.- SECURITY.md now states scope, a five-business-day acknowledgement target, a 90-day coordinated disclosure window, and a good-faith research statement.
Fixed
examples/gemini-interactions.mjscrashed on its own error path by assigning to an imported binding.- The Cursor plugin logo path pointed at a directory that does not exist relative to the manifest.
- The Gemini examples allowed three tools while the extensions publish four.
Removed
- Internal marketplace submission notes, launch plans, and status logs that were checked into
submission/andCURSOR_MARKETPLACE_SUBMISSION.md. The review fixture moved totest/fixtures/, the screenshots todocs/images/, and the logo toassets/.
1.1.0 - 2026-08-31
Added
get_shopping_decisionjoins the published tool set in every manifest and guide.
1.0.1 - 2026-08-26
Added
- DeepSeek Harness connection example.
1.0.0 - 2026-08-26
Added
- Initial public release: Gemini CLI and Qwen Code extensions, Codex plugin, Agent Plugin manifests, official MCP Registry metadata, provider setup guide, and the WebMCP contracts, runtime, and evaluator.