MCP Server

Last updated: October 2, 2026

BetterReviews is an MCP server (Model Context Protocol, Streamable HTTP transport, protocol version 2025-06-18). Instead of writing curl calls, an AI client gets a small set of tools over your store: search the review corpus, read a review, pull the best reviews for a product, record and manage marketing claims, link supporting reviews, ask a reviewer for permission to feature their review, and run the whole product-page optimization loop — regenerate, edit, fact-check, approve and publish.

The server is stateless and plain JSON: every POST carries one JSON-RPC 2.0 message and gets one JSON response; a notification gets 202 with no body. There are no sessions, no server-sent events and no batching.

Two ways to connect

DoorURLAuthTools
Headerhttps://api.betterreviews.app/api/v1/mcpX-API-Key header with a store API keyAll sixteen (ten need Read & Write)
Connector URLhttps://api.betterreviews.app/api/v1/mcp/c/<token>The URL itself (a 90-day connector token)Read-only six

Claude Code

claude mcp add --transport http betterreviews https://api.betterreviews.app/api/v1/mcp \
  --header "X-API-Key: YOUR_API_KEY"

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "betterreviews": {
      "type": "http",
      "url": "https://api.betterreviews.app/api/v1/mcp",
      "headers": { "X-API-Key": "YOUR_API_KEY" }
    }
  }
}

claude.ai custom connector

claude.ai connectors cannot send headers, so you use a connector URL instead. Mint one with a Read & Write API key (201):

curl -X POST https://api.betterreviews.app/api/v1/mcp/connector-tokens \
  -H "X-API-Key: YOUR_API_KEY"
{
  "id": "…",
  "name": "mcp-connector",
  "prefix": "ppo_AbCd",
  "expires_at": "2026-12-12T10:00:00Z",
  "url": "https://api.betterreviews.app/api/v1/mcp/c/ppo_…"
}

Paste url as the connector URL in claude.ai. It is shown once. Then:

  • Treat the URL as a password. Anyone who has it can read your reviews and claims until it expires or you revoke it.
  • It is read-only: only search_reviews, get_review, top_reviews, list_claims, permission_preview and get_optimization are available. Mutating tools are not listed and return read_only_token if called.
  • It expires after 90 days; at most two are active per store (422 too_many_connector_tokens).
  • It only works at its own URL. Presented as a header, Bearer token or query parameter anywhere on the REST API it is refused with 401.
  • Revoke it from Settings → Developer in the BetterReviews app (it appears as mcp-connector) or with DELETE /api/v1/mcp/connector-tokens/:id. Revocation is immediate.

Managing connector tokens requires a Read & Write API key: POST mints, GET /api/v1/mcp/connector-tokens lists the active ones as {"data": [...]} (id, prefix, last used, expiry — never the URL), DELETE /:id revokes.


Tools

ToolAuthArgumentsReturns
search_reviewseitherquery (required, ≤500 chars), product_id, min_rating 1–5, limit 1–50reviews[], total, has_more. Natural-language search over the corpus. Requires an active paid subscription — the Free plan gets subscription_required.
get_revieweitherreview_idRating, title, content, display name, status, stage, rights_status, tags, media count. Never the reviewer's email.
top_reviewseitherproduct_id (required, ≤64 chars), objective (marketing | helpful, default marketing), limit 1–20 (default 3), min_rating 1–5, exclude_ids (≤50 review IDs){reviews[], tier, coverage, objective}. The best reviews for one product, ranked in SQL — no model call, no AI cost. Each row carries id, product_id, rating, title, content, reviewer_display_name, is_verified_buyer, rights_status, word_count, quality_score, review_created_at and media_count — the same key names as get_review, not the REST endpoint's created_at/media[] shape. There is no require_media argument (REST only), and limit defaults to 3 here versus 5 on REST. tier is A (some reviews AI-analyzed) or C (none yet, analysis-free ranking); coverage is {total, analyzed}. Product-scoped: Review Group siblings are never pooled in. Capped at 60 calls/minute per store on top of the shared limit, in a bucket shared with GET /reviews/top.
list_claimseitherproduct_id, status, collect_enabled, include_archived, limit, offsetSame shape as GET /claims.
permission_previeweitherreview_id{reachable, masked_email} — the full address never leaves the server.
upsert_claimheader + writeCreate: product_id, claim_text, topic, source (+ optional ask_phrasings, status, collect_enabled). Update: claim_id + fields.{claim}. Same validation as the REST API (invalid_prompt_text, product_not_found, another_claim_active).
set_claim_collectionheader + writeclaim_id, enabled{claim}. One collect-enabled claim per product.
link_reviews_to_claimheader + writeclaim_id, reviews[] of {review_id, evidence_quote?, confidence?} (≤50){claim_id, reviews[], links_total} — the links written by this call plus the claim's total link count.
request_review_permissionheader + writereview_id, scope (text | photo | both, default both){permission_request_id, status}. Consent rules apply: unreachable, opted-out or suppressed reviewers are refused.
get_optimizationeitherproduct_idThe latest product-page optimization: the five widgets, status, version and the fact-check report. Same body as GET /products/:id/optimization.
regenerate_optimizationheader + writeproduct_id, blocks[] (≤5), feedback (≤1000 chars), featured_review_idThe new draft's summary with accepted: true — generation runs in the background. Costs credits past the free regenerations per block; needs an active subscription. featured_review_id requires top_review to be among the blocks (invalid_featured_review_id otherwise); a review that is not eligible leaves the widget empty with a reason in the report rather than failing the call.
edit_optimization_widgetheader + writeproduct_id, widget_key, contentThe updated draft. Edited text is re-fact-checked before it can be published; a byte-identical resubmit changes nothing.
recheck_optimizationheader + writeproduct_idThe draft with a fresh report over the widgets that had not passed. Widgets that already passed are untouched.
approve_optimizationheader + writeproduct_idThe draft, now approved. Publishing is a separate call.
publish_optimizationheader + writeproduct_idThe published optimization. Spends credits and writes the live Shopify metafields; refused with unverified_content while any widget has not passed the fact check.
flag_optimization_widgetheader + writeproduct_id, widget, reason, note{flagged, refunded} plus the regeneration counters. Always recorded; refunds only when the fact-check report agrees.

The ten header + write tools also require the key's Read & Write scope — a Read-only key sees them in tools/list but calling one returns insufficient_scope.

Claim, permission and optimization tools are entitlement-gated (claim_collection, rights_management, product_pages) exactly like the equivalent REST operations; without the entitlement the tool returns entitlement_required. Better and Studio include them. On legacy plans (no longer sold), they are available on Growth and above, and on legacy Pro and Enterprise; stores on the legacy Starter plan do not have them. The Free plan does not include them, and neither does a cancelled store (cancelling returns the store to Free).

Errors

Two layers, matching the MCP specification:

  • JSON-RPC errors for protocol problems: -32601 unknown method, -32602 unknown tool or invalid arguments (wrong type, over-long string, out-of-range number, too many items — checked before any tool runs), -32600 for batch arrays or malformed requests, -32700 for an empty body.
  • Tool results with isError: true for domain outcomes. The text content and structuredContent carry {"error": "snake_case_code"} using the same codes as the REST API: review_not_found, claim_not_found, claim_archived, product_not_found, another_claim_active, invalid_prompt_text, validation_error, missing_fields (with a fields array — upsert_claim create needs product_id, claim_text, topic and source), invalid_search, invalid_product_id (top_reviews — not numeric after stripping a shopify- prefix or a product GID), entitlement_required, subscription_required, read_only_token, insufficient_scope (mutating tool with a Read-only key), immutable_fields (product_id / source cannot change on update), rate_limited, search_unavailable, and the permission-request outcomes (no_reachable_email, consent_blocked, suppressed, already_requested). The optimization tools call the same implementation as their REST endpoints, so they answer the same codes: optimization_not_found, optimization_in_progress, optimization_changed, insufficient_credits, credit_limit_reached, unverified_content, invalid_blocks, invalid_featured_review_id, invalid_widget_key, invalid_content, legacy_draft_regenerate and unpublished_optimization_limit.

HTTP-level: 401 bad/expired/revoked key or token, 403 credential without a store, 413 body over 256 KB, 429 rate limited (120 requests/minute per store on each door; top_reviews adds a 60/minute per-store bucket shared with GET /reviews/top). Some tools also draw on per-store hourly buckets shared with the equivalent REST endpoints, so one quota covers both doors: optimization_generate 30/h (regenerate_optimization), optimization_verify 60/h (recheck_optimization and a non-empty edit_optimization_widget), optimization_feedback 20/h (flag_optimization_widget) and optimization_publish 30/h (publish_optimization); request_review_permission is capped at 10/minute and 200/day per store. Exceeding any of these returns rate_limited as a tool result. GET returns 405; DELETE returns 204.

Workflow: does our corpus support this claim?

With the header door connected in Claude Code, a typical exchange is:

  1. search_reviews — "keeps drinks hot for hours", filtered to the product.
  2. get_review on the promising hits to read the full text.
  3. upsert_claim — record the claim with a neutral topic and example ask_phrasings.
  4. link_reviews_to_claim — attach the supporting reviews with the quoted sentence and a confidence.
  5. Not enough evidence? set_claim_collection so the AI review chat can ask happy customers about the topic. Enough? permission_preview + request_review_permission before quoting a review in ads.

See the Claims API for field semantics and the API keys guide for creating and revoking keys.