MCP server
Harvv runs a hosted Model Context Protocol server so AI coding agents (GitHub Copilot, Cursor, Claude, Windsurf, Zed, Lovable) can read your behavioral findings and install the pixel without leaving the editor. One endpoint, one bearer token, scoped to your account.
Endpoint
https://harvv.com/mcp
The aliases /mcp/lovable and /mcp/harvv resolve to
the same server. Point any MCP client at the base /mcp URL.
Connect your client
Claude and ChatGPT sign in with OAuth: you paste the server URL and sign in
with your Harvv email, with no token to copy. Other clients use the standard
MCP config, an HTTP server URL plus an Authorization: Bearer
header. Only the file or UI you paste it into changes.
Claude (claude.ai, Claude Desktop, Claude mobile)
- In Claude, open Customize → Connectors and choose Add custom connector (or pick Harvv from the directory).
- Enter
https://harvv.com/mcp, leave the OAuth fields blank, and click Add. No token is needed: Claude sends you to harvv.com to sign in (OAuth). - Sign in to Harvv and approve. Claude gets a token for your account only, and renews it itself.
- Ask about your site in a new conversation, for example "What sites do I have in Harvv?" or "Why are sales down this week on mystore.com?"
Plans. The free Claude plan can add one custom connector; Pro and Max add their own. On Team and Enterprise an Owner adds Harvv once under Organization settings → Connectors (Add, then Custom), and each member finds it under Customize → Connectors and clicks Connect.
Pasting a token instead. Claude only takes a fixed token through the Request headers section of the Add custom connector dialog, a beta that only some organizations have. If you have it and want a token, set Authentication to No sign-in, create a token in Harvv under Settings → Integrations and enter Bearer, a space, and the token as the authorization header. Otherwise use the sign-in above.
To disconnect, remove the connector in Claude, or revoke it in Harvv under Settings. Plain-English setup with screenshots: harvv.com/claude.
Other clients
- VS Code / GitHub Copilot, Cursor, Claude Desktop, Windsurf, Zed, Copilot CLI: use the generated config at /install/copilot. It fills in your token and shows the right file per client.
- Lovable: open Connectors, pick Harvv from the catalog, and paste your token. Walkthrough at /install/lovable.
- A custom client or your own agent: see Transport below.
Authentication
- Generate a token at Studio → Settings → Integrations (or right on /install/copilot). The raw token is shown once.
- Each token is scoped to one Harvv account. Every tool call filters to that account's sites and no one else's.
- Token lifetime is yours to choose when you create one in the dashboard (no expiry, 30 days, 90 days, or a year). Connectors set up through OAuth (claude.ai, ChatGPT, Claude Code) get a 90-day token plus a refresh token and renew themselves. Tokens created before September 2026 never expire. A 401 carries a reason code that says which case you hit. See Troubleshooting.
- Keep the token out of committed config. The VS Code flow uses an input prompt so the editor stores it for you.
Transport (custom clients)
The server speaks JSON-RPC 2.0 over Streamable HTTP (MCP spec 2025-03-26 and later). For a hand-rolled client:
- Method:
POSTtohttps://harvv.com/mcp - Headers:
Content-Type: application/json,Authorization: Bearer YOUR_TOKEN, andAccept: application/json, text/event-stream(the MCP SDK 1.25+ enforces the dual Accept value). - Lifecycle:
initialize, thentools/list, thentools/call.pingis supported.
curl -X POST https://harvv.com/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"list_my_sites","arguments":{}}}'
Tools
49 tools, listed from the same registry tools/list
serves, so this page cannot drift from the server. Every tool is scoped to
the account behind your token. Most only read. 7 save a setting you ask for (dismiss_issue, mark_issue_fixed, annotate_change, set_conversion_goal, set_brand_names, set_working_context, set_costs): each returns a preview and changes nothing unless called with confirm=true, is marked destructive so your client asks before it runs. Dismissals, fixes, goals, costs and brand names can be changed back in Harvv; a change note saved with annotate_change stays. Someone given a single site to view cannot change it. Tools that act on a site take
site_id; on an account with more than one site it is required,
and list_my_sites is how you get it. The setup tools and
get_help are free; data tools count against the monthly
credit allowance only when they answer (a timeout or a bad argument is not
charged).
Setup and help
Free. Onboarding, verification, navigation, the product guide, and a self-check for a draft conclusion.
check_install
Checks whether the Harvv pixel is firing on a public URL by fetching the page.
url(required): Public URL of the published site, http or https only. Localhost, RFC1918 ranges, and cloud-metadata IPs are rejected. Lovable preview URLs (id-preview--*.lovable.app) will return installed=false with a tailored message b
check_slack_integration (no arguments)
Checks whether this account's Slack integration is wired up correctly by making live calls to Slack, so it reports what Slack actually returns rather than what Harvv's database records.
get_connect_link
Get a one-time link that connects Google Analytics (GA4) or Google Search Console to one site on this account.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqintegration(required): What to connect: 'ga4' (Google Analytics), 'search_console' (Google Search Console), or 'google_ads', 'meta_ads', 'slack' (no one-time link; returns the Settings link).
get_dashboard_url
Returns a link that opens a site in the Harvv dashboard, where the full behavioral data, session lists and charts are.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_req
get_help
Harvv product documentation and best practice, not site data.
question(required): The user's question in their own words, e.g. 'how do I turn off the slack integration'.
install_pixel
Returns the Harvv pixel install snippet for a site on this account, with where to place it on each platform (Shopify, WordPress, Webflow, Squarespace, Wix, Google Tag Manager) and code framework.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_req
list_my_sites (no arguments)
Lists every site on the Harvv account this connection is scoped to, with id, name, domain and pixel_key for each.
red_team
Returns Harvv's checklist of the ways its own numbers have been wrong in production (impossible counts, missing denominators, stale figures quoted as current, causes asserted but never measured, measurement gaps reported as zero, raw-count rankings that only f
draft_conclusion(optional): (optional) The conclusion about to be presented; the checks come back ordered by how likely each is to apply to it.site_id(optional): (optional) The site the draft is about: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, Harvv takes it from the tools called just before in this conversation.
red_team_report
Records the outcome of a Red Team pass (a red_team call whose checks were applied to a draft): one finding per claim checked, with the check_id that applied, the claim in a sentence, the verdict (survives, softened, retracted), why, and source_tool when known,
run_id(required): The run_id red_team returned.findings(optional): One per claim you checked, at most 30.checked(optional): The check ids you applied, when no claim changed.revised_conclusion(optional): (optional) The conclusion after the checks, when it changed.
Data tools
Metered. Behavioral findings, page-level friction and behavior, traffic, conversion, speed, SEO and AI visibility for your sites. Every numeric response carries window, counted and sample blocks that say what was measured.
annotate_change
Saves a dated note about a change made to the site (a deploy, a theme edit, a campaign launch, a price change), so later comparisons covering that date show it beside a rise or drop.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdate(required): When it happened, YYYY-MM-DD.note(required): What changed, one or two sentences.confirm(optional): true saves; omitted or false returns a preview and saves nothing.
dismiss_issue
Dismisses a finding on a site in this account, with a reason, the same as the dashboard's Dismiss button.
issue_id(required): The finding id from get_issues / get_friction_findings.reason(required): Why it is being dismissed.confirm(optional): true dismisses the finding; omitted or false returns a preview and changes nothing.note(optional): (optional) A one-line note for the activity log.
get_ad_spend
What a site's ads cost and what the ad platform reports they returned: spend, impressions, clicks, conversions and conversion value per campaign, a daily series, and the change history (who changed a budget, bid strategy, status, landing page, ad or keyword, a
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 30, max 90.detail(optional): (optional) 'compact' (default): every ranked row with the fields an answer needs, plus a `detail` block saying what was left out; small and quick to read. 'full': adds the evidence prose and per-row detail; several times
get_ad_traffic_quality
How the traffic from each ad campaign behaves after it lands, compared with the site's own non-paid traffic: bounce, rage clicks, dead clicks, scroll depth and form interaction, over the last N days (default 30, max 90).
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 30, max 90. Wider than other tools on purpose: a campaign sending 30 sessions a day cannot be separated from noise in 7 days.campaign(optional): (optional) One campaign by name (exact, or a substring; case does not matter) to get its landing-page split: every judged page, plus the largest pages too small to judge, with the rest counted. The default answer lists e
get_ai_bot_activity
AI crawler activity on a site over the last N days (default 30, max 90), including robots.txt violations (requests for paths robots.txt disallows).
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 30, max 90 (clamped).
get_ai_recommendations
Which pages AI assistants (ChatGPT, Perplexity, Gemini, Claude, Copilot) sent visitors to, and whether those pages carry structured data (JSON-LD) a model can quote.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 90, max 365. Needs ~90 days to clear the reporting floor on most sites.
get_audience_state
Who a site's visitors are, as categories, over the last N days (default 7, max 30): signed-in vs anonymous vs unknown, whether logged-in CMS staff are in the numbers, whether visitors arrived with a cart, and first-time vs returning.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 7, max 30 (clamped).
get_commerce_funnel
The site's commerce funnel (browsed collection, viewed product, added to cart, viewed cart, began checkout, purchased) plus form starts and submits, over the last N days (default 7, max 30), with session and event counts and cart value where tagged, built from
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 7, max 30 (clamped).device(optional): (optional) Narrow every step to one device class. Omit for all devices; `by_device` compares them either way.
get_conversion_path
Finds the interaction that gates conversion on a page and how many visitors never reach it, by grouping the page's sessions by what they clicked before converting and comparing each group with an engagement-matched control.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqpath(optional): (optional) Page path to analyse, e.g. /products/some-item, or / for the homepage. Matched exactly by default; path /products with path_match 'prefix' covers every product page. Omit to analyse the whole site. Same name apage(optional): (deprecated alias of path; accepted for one release)path_match(optional): (optional) How `path` is matched. 'exact' (default): that one page only, so '/' is the homepage. 'prefix': the page and every page under it, e.g. path '/products' with 'prefix' covers all product pages. 'contains': any pdays(optional): (optional) Window in days. Default 7, max 30 (clamped). Keep it small on high-traffic stores.conversion(optional): (optional) What counts as a conversion. Default 'auto' = a click on a buy/submit control (add to cart, buy now, checkout, place order). Or pass a substring to match a specific element, e.g. 'Add to Cart'.detail(optional): (optional) 'compact' (default): every ranked row with the fields an answer needs, plus a `detail` block saying what was left out; small and quick to read. 'full': adds the evidence prose and per-row detail; several times
get_conversions
Conversions, and revenue where the site sells online, for questions like 'how are sales today' or 'are leads coming in'.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 30. Trends compare this window against the one immediately before it.device(optional): (optional) Narrow Harvv's own orders and leads to one device class. GA4 is not split by device, so its fields are left out when this is set.
get_dismissed_findings
What this account has already dismissed on a site: findings marked dismissed with the reason given (false positive, noise, intentional, duplicate, fixed already, not useful) or auto-expired by housekeeping.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqlimit(optional): (optional) Max findings, default 20, max 50.
get_element_clicks
Which elements get clicked most, ranked by click volume, optionally narrowed to a page path or an element-name filter (element_contains 'footer' answers 'what do people click in the footer?').
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqpath(optional): (optional) Page path to narrow to, e.g. /cart, or / for the homepage. Matched exactly by default; see path_match.path_match(optional): (optional) How `path` is matched. 'exact' (default): that one page only, so '/' is the homepage. 'prefix': the page and every page under it, e.g. path '/products' with 'prefix' covers all product pages. 'contains': any pelement_contains(optional): (optional) Only elements whose handle contains this text, e.g. 'footer' or 'add-to-cart'.device(optional): (optional) Narrow to one device class: 'mobile', 'desktop' or 'tablet'. Omit for all devices. The response carries device_coverage: device was only recorded on this rollup from 2026-09-01, so a filtered read over an oldesegment(optional): (optional) Narrow to one session segment, e.g. 'logged_in', 'anonymous', 'has_cart', or a merchant-declared value namespaced by its window.harvv.user key like 'tags:wholesale' or 'plan:pro'. A value never observed on thedays(optional): (optional) Window in days. Default 7, max 30 (clamped).limit(optional): (optional) Max elements returned. Default 12, max 25.
get_fleet_benchmark
Compares a site with the Harvv fleet on five behaviour rates over the last 30 days: share of sessions on phones, sessions with a dead click, sessions with a rage click, sessions that never scroll past a quarter of the page, and dead clicks per 1,000 sessions.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_req
get_forms
Every form visitors use on a site, one row per form: sessions that started it, progress through a multi-step form (sessions per step), what Continue/Submit clicks did (refused with errors, moved on, finished, left the page, closed the popup, or nothing visible
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqpath(optional): (optional) Page path the form is on, e.g. / or /contact. Matched exactly by default; see path_match. Omit for every page.path_match(optional): (optional) How `path` is matched. 'exact' (default): that one page only, so '/' is the homepage. 'prefix': the page and every page under it, e.g. path '/products' with 'prefix' covers all product pages. 'contains': any pform(optional): (optional) One form's key exactly as a previous get_forms call returned it in `form`, e.g. div#signup or form#contact.days(optional): (optional) Window in days. Default 7, max 30 (clamped).
get_friction_findings
Alias of get_issues, kept for older clients; get_issues returns the same findings plus fix text and a status filter.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 7, minimum 1, maximum 30. Values outside this range are clamped; the response will include clamped=true.
get_issue_sessions
Lists the visitor sessions affected by one issue (the issue_id from get_issues), plus a dashboard link showing all of them.
issue_id(optional): The issue_id from get_issues (or the id in a finding's dashboard_url). Required in practice: without it there is no finding to list sessions for.site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqlimit(optional): (optional) Max session ids to return. Default 50, max 200.
get_issues
The UX issues Harvv has detected on a site, each with what is broken, sessions affected, status, and a plain-English recommended fix.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqstatus(optional): (optional) 'open' (default) = the active work pipeline; 'all' = everything not dismissed; 'resolved' = only resolved and fixed issues; 'dismissed' = what was closed without a fix (false positive, won't fix, noise), whichlimit(optional): (optional) Max issues to return. Default 15, max 50. The answer also stops at the size an AI assistant accepts from one call; when it does, or when more findings match, `next_offset` says where the rest start.offset(optional): (optional) Skip this many findings in the ranked list, to page. Pass the `next_offset` from the previous answer. Default 0.include_headline(optional): (optional) Also return a `headline` block: sessions, dead clicks and rage clicks over `days` (default 7) for the whole site. This is what get_friction_findings used to add.days(optional): (optional) Window for the headline block only. Default 7, max 30. Findings are never filtered by it.detail(optional): (optional) 'compact' (default): every ranked row with the fields an answer needs, plus a `detail` block saying what was left out; small and quick to read. 'full': adds the evidence prose and per-row detail; several times
get_js_errors
JavaScript exceptions and failed network requests on a site, grouped by message and file or by URL, over the last N days (default 3, max 30).
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqpath(optional): (optional) Page path to narrow to, e.g. /checkout, or / for the homepage. Matched exactly by default; see path_match.path_match(optional): (optional) How `path` is matched. 'exact' (default): that one page only, so '/' is the homepage. 'prefix': the page and every page under it, e.g. path '/products' with 'prefix' covers all product pages. 'contains': any pdays(optional): (optional) Window in days. Default 3, max 30 (clamped). On a very high-traffic site the window is narrowed to what fits one answer (1 day on the busiest store) and the answer says so in `degraded`.limit(optional): (optional) Max rows per list. Default 10, max 25.detail(optional): (optional) 'compact' (default): every ranked row with the fields an answer needs, plus a `detail` block saying what was left out; small and quick to read. 'full': adds the evidence prose and per-row detail; several timesdevice(optional): (optional) Only sessions on this device type.segment(optional): (optional) Only sessions carrying this visitor segment. Unknown segments are refused with the observed list.
get_keyword_opportunities
Ranked SEO opportunities for a site: every search query Google Search Console reports the site appearing for, joined to Google Ads search volume, CPC and competition.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqlimit(optional): (optional) How many opportunity rows to return. Default 25, max 100.min_volume(optional): (optional) Ignore queries below this monthly search volume. Default 0 (keep everything).
get_keyword_volume
Monthly Google search volume, CPC, competition and 12-month trend for keywords you name, for a country, state or city.
keywords(required): Keywords to look up. Up to 150 per call (what one answer holds). Case and extra whitespace are normalised, duplicates collapse to one.location(optional): (optional) The market to research (not the user's own location), by name: "Phoenix, Arizona", "Anthem, AZ", "Arizona", "Toronto, Canada". Resolved to Google's own location (city, neighbourhood, county, state or country);location_code(optional): (optional) A Google location code, when one is already known. Default 2840 (United States). `location` resolves a place name to its code.language_code(optional): (optional) Language code. Default "en".
get_layout_changes
Estimates when a site's layout or theme last changed, and which open findings that change may have invalidated, from the pixel alone (no CMS access or plugin needed).
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days, like every other tool. Default 56 (8 weeks), max 182. Scanned in whole weeks.weeks(optional): (deprecated alias of days; whole weeks)
get_page_behavior
How visitors behave on a page (or the whole site) beyond clicks: the share who left mid-visit to check something elsewhere (tab-away), who hesitated (with the median pause), who copied text such as a price or SKU, who held the site open in several tabs, who re
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqpath(optional): (optional) Page path to narrow to, e.g. /cart, /products/some-item, or / for the homepage. Matched exactly by default; see path_match. Omit for site-wide. When set, the response carries path_scope (how many pages were mapath_match(optional): (optional) How `path` is matched. 'exact' (default): that one page only, so '/' is the homepage. 'prefix': the page and every page under it, e.g. path '/products' with 'prefix' covers all product pages. 'contains': any pdays(optional): (optional) Window in days. Default 7, max 30 (clamped).
get_page_friction
Per-page friction for a site over the last N days (default 7, max 30): dead clicks, rage clicks and affected sessions per page, plus the most-stuck elements.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 7, max 30 (clamped).path(optional): (optional) A page path (e.g. /merchants, or / for the homepage) or full URL. When set, returns the top dead/rage-clicked elements on that page in target_page_top_elements, so you can name which elements are stuck on a sppath_match(optional): (optional) How `path` is matched. 'exact' (default): that one page only, so '/' is the homepage. 'prefix': the page and every page under it, e.g. path '/products' with 'prefix' covers all product pages. 'contains': any pdevice(optional): (optional) Narrow to one device class: 'mobile', 'desktop' or 'tablet'. Omit for all devices. Friction is usually device-specific -- a control that is fine on desktop and unreachable on mobile reads as mild sitewide fricsegment(optional): (optional) Narrow to one session segment, e.g. 'logged_in', 'anonymous', 'has_cart', or a merchant-declared value namespaced by its window.harvv.user key like 'tags:wholesale' or 'plan:pro'. A value never observed on the
get_page_ui
Renders one site page as a shopper sees it (phone by default), never typing or submitting: main buttons and how far down they sit, disabled controls and messages, popups and close size, small tap targets.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqpath(optional): (optional) A path on the site, e.g. /cart or /products/x, or a full URL on its domain. Default /.device(optional): (optional) mobile (default) or desktop.
get_performance
Core Web Vitals for a site from real visitor sessions over the last N days (default 7, max 30): LCP, INP, CLS and TTFB at p50 and p75, plus the slowest pages by LCP.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 7, max 30 (clamped).device(optional): (optional) Only sessions on this device type.segment(optional): (optional) Only sessions carrying this visitor segment. Unknown segments are refused with the observed list.
get_profit
Profit per day and for the window: revenue, product revenue net of tax and shipping, estimated cost of goods, shipping cost, payment fees, ad spend (Meta and Google, the same import get_ad_spend reads), fixed costs, and what is left.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Whole days ending yesterday. Default 30, max 90.start_date(optional): (optional) First day, YYYY-MM-DD in the store's time zone. Overrides days.end_date(optional): (optional) Last day, YYYY-MM-DD. Default yesterday.split_date(optional): (optional) A change date, YYYY-MM-DD: adds per-day averages before it and from it on.
get_search_console
Google Search Console data Harvv has synced for a site: totals, top queries by clicks, CTR leaks (queries ranking well that get fewer clicks than their position predicts), almost-ranking queries and pages (average position 11 to 20), and how visitors arriving
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqlimit(optional): (optional) Max rows per list. Default 20, max 50.
get_seo_insights
Per-page SEO and GEO (AI-search readiness) findings for a site, built from what the pixel captured on the real rendered page of each URL visitors loaded, over the last N days (default 14, max 30).
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 14, max 30 (clamped).detail(optional): (optional) 'compact' (default): every ranked row with the fields an answer needs, plus a `detail` block saying what was left out; small and quick to read. 'full': adds the evidence prose and per-row detail; several times
get_setup_status
Where this Harvv account's setup stands, from what Harvv has observed rather than what anyone reports: for every site, whether the pixel has ever fired (and whether it is quiet), whether a Shopify store's Customer Events checkout pixel is delivering checkout s
site_id(optional): (optional) One site to check: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the checklist covers every site on the account.
get_site_stack
The site's technology stack as Harvv last observed it: platform, theme, and the active plugin list with the role of each.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqcategory(optional): (optional) Filter to one category, e.g. 'page_builder', 'seo', 'security', 'caching'.
get_stuck_points
Where shoppers get stuck between cart and order: per step, sessions that reached and passed it; per stuck element (dead, rage, disabled or repeated taps), who struggled and left, where they went, a range of extra sessions and orders a month (observational), wh
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 30, max 30 (clamped).device(optional): (optional) Only sessions on this device class. Phones are where most checkout friction shows.step(optional): (optional) Only stuck points on this step.
get_traffic_sources
Where a site's traffic comes from over the last N days (default 7, max 30), classified into channels (Organic search, Paid search, Paid social, Social, Email, Referral, Direct) with sessions and share for each.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 7, max 30 (clamped).
get_traffic_summary
Traffic overview for a site over the last N days (default 7, max 30): sessions, unique visitors, top pages, referrer and source split, bounce-equivalent, the device split (`devices`: mobile, desktop, tablet) and the browser split (`browsers`: safari, chrome, e
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Window in days. Default 7, max 30 (clamped).device(optional): (optional) Only sessions on this device type.browser(optional): (optional) Only sessions in this browser. The response's `browsers` split shows which browsers the site actually gets.segment(optional): (optional) Only sessions carrying this visitor segment (e.g. logged_in, anonymous, first_visit, has_cart). Unknown segments are refused with the observed list.
mark_issue_fixed
Records that a finding has been fixed, the same as the dashboard's Mark fixed action.
issue_id(required): The finding id.confirm(optional): true marks the finding fixed; omitted or false returns a preview and changes nothing.note(optional): (optional) What was changed, for the activity log.
report_page_walk
Records the steps an assistant walked for a browser_handoff run_id: element, size, disabled or unresponsive, nearby message, what stopped it.
run_id(required): The run_id from browser_handoff.steps(required): One per step walked, at most 25.
set_brand_names
Saves the names a business is searched by besides its domain: a former name, a store name, or a domain it used to live on (e.g.
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqnames(required): The names (or old domains) the business goes by, 3 to 60 characters each, at most 20.confirm(optional): true saves; omitted or false returns a preview and saves nothing.
set_conversion_goal
Sets what counts as a conversion on a site: 'form' (a form submit, call or booking), 'add_to_cart' (a store's cart and checkout) or 'cv' (the site sends its own conversion event).
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqgoal(required): What a conversion is on this site.confirm(optional): true saves; omitted or false returns a preview and saves nothing.
set_costs
Saves what a store's products, shipping and payments cost, so get_profit can say whether a change made or lost money (revenue and profit can move in opposite directions).
site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdefault_cost_pct(optional): Cost of goods as a % of item price, for any product without its own entry (0 to 100). null clears it.cost_by_type(optional): Per product type: [{ product_type, cost_pct } or { product_type, cost_per_item }], at most 50. Replaces the saved list; an empty list or null clears it.shipping_cost_per_order(optional): What the store pays to ship one order (0 if the customer pays all of it). null clears it.prices_include_tax(optional): true if shelf prices already include sales tax (common outside the US), false if tax is added at checkout.payment_fee_pct(optional): Card processing % of the order total (0 to 20), e.g. 2.9.payment_fee_fixed(optional): Fixed card fee per order, e.g. 0.30.fixed_costs_per_day(optional): Costs that do not move with sales (rent, staff, apps), per day. 0 gives profit before overheads.confirm(optional): true saves; omitted or false returns a preview and saves nothing.
set_working_context
Saves notes about who a person works with, so later answers are framed for the right reader.
scope(required): 'this_client': who decides on one site, plus notes about them. 'me': a note about the person themselves.site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqwho_decides(optional): (scope 'this_client') Who makes the final call on this site's findings.notes(optional): Up to 1000 characters, in the person's own terms. Replaces the saved note; an empty string clears it; omit to keep it.confirm(optional): true saves; omitted or false returns a preview and saves nothing.
verify_finding
Re-checks one finding against recent data (default the last 3 days, max 14) to answer 'is this still happening now?'.
issue_id(optional): The issue_id from get_issues. Required in practice.site_id(optional): (optional) The site to read: its domain (e.g. example.com), 16-character pixel key, or site UUID. When omitted, the account's only site is used if it has exactly one; an account with several sites returns reason site_reqdays(optional): (optional) Recent window to re-check. Default 3, max 14. Short on purpose: the question is whether it is happening NOW.
Example: get_friction_findings
Request:
{"jsonrpc":"2.0","id":4,"method":"tools/call",
"params":{"name":"get_friction_findings","arguments":{"days":7}}}
The result is a content block whose text is JSON:
{
"ok": true,
"site": { "id": "c8d5fc24-...", "name": "Acme", "domain": "acme.com" },
"window": { "days": 7, "requested_days": 7, "clamped": false,
"min_days": 1, "max_days": 30 },
"headline": { "sessions": 1840, "events": 41230,
"dead_clicks": 212, "rage_clicks": 64,
"unit": "count", "window_days": 7 },
"findings": [
{ "id": "8231", "type": "dead_click",
"title": "Dead clicks on pricing CTA",
"element": "button.upgrade-cta", "priority": "high",
"summary": "212 clicks on a button with no handler ...",
"dashboard_url": "https://harvv.com/newdashboardui#/site/c8d5fc24-.../issue/8231" }
]
}
All headline counts are totals over the whole window, not
per-day averages. Findings come sorted high priority first.
Troubleshooting
token_expired: the token reached the lifetime it was created with. An OAuth connector refreshes on its own; a pasted token is regenerated at Studio → Settings → Integrations.token_revoked: regenerate at Studio → Settings → Integrations and reconnect.token_not_found: mistyped, or from another account. Regenerate and reconnect.wrong_scope: not a customer MCP token. Generate one from your dashboard.
error.data.reason (JSON-RPC) and the
WWW-Authenticate response header.
inputSchema looks
like {"properties":{}}, it cached an older catalog from before
the site_id / url / days parameters
shipped. Remove and re-add the Harvv connector (or refresh the connection)
to re-fetch tools/list and pick up the current schemas.
list_my_sites genuinely takes no arguments, so an empty schema
there is correct.
check_install reports not-installed on a Lovable preview.
Use the published URL, not the id-preview--*.lovable.app wrapper
(see check_install above).
Signing in from Claude lands on the Harvv dashboard instead of returning to Claude. Start the connection again from Claude; the sign-in page returns you to the approval step. If your browser is signed in to a different Harvv account, the approval page offers to switch.
Data and privacy
- Tool results contain aggregated figures about your own sites (counts, rates, percentiles, element names, page paths). The pixel records no names, email addresses or typed text; see the privacy policy.
- Results go only to the AI client you connected. Harvv keeps none of your conversation: for each call it logs the tool name, the argument names (not their values), timing, response size and the client's name, to run and support the server.
red_teamkeeps the ids of the checks a draft triggered and, ifred_team_reportis used, each check's verdict and the tool named. It keeps no text from the draft or the conversation.get_keyword_volumeandget_keyword_opportunitiessend the keywords and location to DataForSEO to look up search volume. No other tool calls a third party exceptcheck_install(fetches the URL you give it) andcheck_slack_integration(calls Slack for your own workspace).- Revoke access any time: remove the connector in your AI client, or revoke it in Harvv under Settings.
Support
Email hello@harvv.com. Report a security issue to jordan@harvv.com (the contact on harvv.com/trust). Privacy requests: privacy@harvv.com.