# Tools reference

<!-- GENERATED by packages/mcp/src/docs/generate-tool-docs.ts from the live tool
     registrations and packages/mcp/src/shapes.ts. Do not edit by hand:
     run `npx pnpm --filter @cro-tool/mcp docs:tools`. -->

# Tools reference

Liftwire exposes 11 tools over MCP. Every response is JSON in `structuredContent` (and the same JSON as text). Every response also carries `corpus_version` (when the published corpus last changed, or null) and `tactic_count` (how many tactics are published), so your agent always knows how large the evidence base is.

| Tool | What it does | Read-only |
|---|---|---|
| [`site_memory`](#site_memory) | Everything this workspace has done on a site, newest first: audits (including ones still running), changes with their last verification status and failed regression gates, and analytics outcomes. | yes |
| [`audit_page`](#audit_page) | Start a conversion audit of one public page: captures it on desktop and mobile, compares it with the published tactics for its page type and vertical, and measures basics (LCP, CLS, accessibility, CTA position). | no |
| [`audit_site`](#audit_site) | Start an audit of a site's key pages (homepage, pricing, signup, demo and similar, up to max_pages, default 8). | no |
| [`get_audit`](#get_audit) | Read an audit started by audit_page, audit_site or verify_change. | yes |
| [`recommend`](#recommend) | Turn a page audit into a ranked implementation plan: each item names a tactic, its evidence grade, effort, where it goes on the page, its structure, copy patterns and acceptance criteria. | no |
| [`search_tactics`](#search_tactics) | Semantic search over the published tactic library (patterns observed on top SaaS sites, each with an evidence grade). | yes |
| [`get_tactic`](#get_tactic) | Full detail for one tactic: the mechanism (why it works), how to detect it, the implementation spec (where on the page, structure, copy patterns, acceptance criteria), anti-patterns, evidence links and how many corpus sites use it. | yes |
| [`get_examples`](#get_examples) | Published examples of a tactic from named sites: a cropped screenshot of the section (image_url, a signed link valid for image_expires_in seconds), the copy used and commentary. | yes |
| [`verify_change`](#verify_change) | After you have implemented a tactic and the change is live on the public URL, call this to re-capture the page, check whether the tactic is now present, and run five pass/fail regression gates against the page as it was before: accessibility (axe critical+serious must not increase), LCP ≤ max(before × 1.2, 2500 ms), CLS ≤ max(before + 0.05, 0.1), no horizontal overflow at 390 px, and CTA count must not drop. | no |
| [`connect_analytics`](#connect_analytics) | Get a one-time link the human opens in a browser to connect Google Analytics 4 or Search Console (Google sign-in) or Clarity (project API token form) for a site, so verified changes can be measured. | no |
| [`get_outcomes`](#get_outcomes) | Analytics outcomes for verified changes on a site: each metric for the 28 days before and the 28 days after the change, with both periods and their session counts. | yes |

Errors come back as `isError: true` with readable text and `structuredContent: { error, message }`, where `error` is a stable code such as `invalid_url`, `tactic_not_found` or `monthly_audit_limit`.

## site_memory

**Site memory.** Everything this workspace has done on a site, newest first: audits (including ones still running), changes with their last verification status and failed regression gates, and analytics outcomes. Call it at the start of every session about a site, so you continue from previous work instead of re-auditing or re-implementing tactics that already shipped. Changes recorded without a baseline stay "reported"; outcomes appear only once the outcomes job has measured a change that has been live for 28 days.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `url` | string (url) | yes | max 2048 chars; Any URL on the site |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  host: string;
  audits: {
    audit_id: string /* uuid */;
    kind: string;
    status: "queued" | "running" | "ready" | "failed" | "cancelled";
    url: string;
    created_at: string;
    finished_at: string | null;
  }[];
  changes: {
    site_change_id: string /* uuid */;
    tactic_id: string | null;
    slug: string | null;
    status: string;
    url: string;
    reported_at: string;
    verified_at: string | null;
    audit_id: string /* uuid */ | null;
    regressions: string[];
  }[];
  outcomes: {
    site_change_id: string /* uuid */;
    tactic_id: string | null;
    slug: string | null;
    provider: "ga4" | "gsc" | "clarity";
    metric_name: string;
    baseline_value: number | null;
    outcome_value: number | null;
    period_start: string;
    period_end: string;
    sample_size: integer | null;
    baseline_period: {
      start: string;
      end: string;
    };
    outcome_period: {
      start: string;
      end: string;
    };
    baseline_sample_size: integer | null;
    outcome_sample_size: integer | null;
    comparison: {
      relative_change: number | null;
      label: "measured" | "insufficient_sample" | "not_comparable";
      reason: string;
    };
  }[];
  next: string;
}
```

## audit_page

**Audit a page.** Start a conversion audit of one public page: captures it on desktop and mobile, compares it with the published tactics for its page type and vertical, and measures basics (LCP, CLS, accessibility, CTA position). Call this first when the user wants to improve a page. It returns an audit_id immediately; the audit runs in the background (usually 1-3 minutes). Next: poll get_audit with the audit_id every ~20 seconds until status is "ready", then call recommend. Repeating the call with the same URL returns the same audit (reused: true) instead of starting a new one; pass force: true only when the page has changed since the last audit. Each new audit counts towards the monthly audit limit.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `url` | string (url) | yes | max 2048 chars; Full public URL of the page, e.g. https://example.com/pricing |
| `page_type` | "homepage" \\| "pricing" \\| "signup" \\| "demo" \\| "contact" \\| "product" \\| "customers" \\| "about" \\| "comparison" \\| "landing" \\| "other" | no | Override automatic page-type detection |
| `vertical` | string | no | max 50 chars; Business vertical, e.g. b2b_saas |
| `force` | boolean | no | Re-audit even if a result from the last 24 hours exists |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  kind: "page_audit" | "site_audit" | "verify";
  status: "queued" | "running" | "ready" | "failed" | "cancelled";
  url: string;
  poll_after_seconds: integer;
  reused: boolean;
  max_pages?: integer;
  tactic_id?: string /* uuid */;
  usage: {
    audits_used: integer;
    audits_limit: integer;
    period_end: string;
  };
  next: string;
}
```

## audit_site

**Audit a site.** Start an audit of a site's key pages (homepage, pricing, signup, demo and similar, up to max_pages, default 8). Use it when the user asks about the whole site rather than one page; for a single page use audit_page. Each page audited counts towards the monthly audit limit, so pass a smaller max_pages on the free plan. Returns an audit_id immediately. Next: poll get_audit with the audit_id every ~20 seconds; when ready it lists one page audit_id per page; call recommend on the pages that matter most.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `url` | string (url) | yes | max 2048 chars; Any URL on the site; its homepage is used as the starting point |
| `max_pages` | integer | no | 1–8; Pages to audit, 1-8 (default 8) |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  kind: "page_audit" | "site_audit" | "verify";
  status: "queued" | "running" | "ready" | "failed" | "cancelled";
  url: string;
  poll_after_seconds: integer;
  reused: boolean;
  max_pages?: integer;
  tactic_id?: string /* uuid */;
  usage: {
    audits_used: integer;
    audits_limit: integer;
    period_end: string;
  };
  next: string;
}
```

## get_audit

**Get audit status and results.** Read an audit started by audit_page, audit_site or verify_change. While status is "queued" or "running", wait poll_after_seconds and call again. When "ready": a page audit lists tactics present and missing, the basics and the page sections; a site audit lists one page audit_id per page; a verification shows whether the tactic is now present and any regressions. detail="summary" (default) is compact; "sections" adds section headings and which tactics sit in which section; "full" adds evidence quotes. No images are returned here (use get_examples). Next for a ready page audit: call recommend with the same audit_id.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `audit_id` | string (uuid) | yes |  |
| `detail` | "summary" \\| "sections" \\| "full" | no |  |

### Output

**Queued or running**

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  kind: "page_audit" | "site_audit" | "verify" | "corpus_capture";
  url: string;
  usage: {
    audits_used: integer;
    audits_limit: integer;
    period_end: string;
  };
  next: string;
  status: "queued" | "running";
  progress: string | null;
  poll_after_seconds: integer;
}
```

**Failed or cancelled**

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  kind: "page_audit" | "site_audit" | "verify" | "corpus_capture";
  url: string;
  usage: {
    audits_used: integer;
    audits_limit: integer;
    period_end: string;
  };
  next: string;
  status: "failed" | "cancelled";
  error: string | null;
}
```

**Page audit ready**

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  kind: "page_audit";
  url: string;
  usage: {
    audits_used: integer;
    audits_limit: integer;
    period_end: string;
  };
  next: string;
  status: "ready";
  detail: "summary" | "sections" | "full";
  page_type: string;
  vertical: string | null;
  captured_at: string | null;
  present: {
    tactic_id: string;
    slug: string;
    confidence: number;
    section: string | null;
    evidence?: {
      quote: string;
      section_kind: string | null;
    }[];
  }[];
  missing: {
    tactic_id: string;
    slug: string;
    grade: "observed" | "tested" | "measured";
    seen_on_sites: integer;
  }[];
  basics: {
    lcp_ms: number | null;
    cls: number | null;
    axe: {
      critical: integer | null;
      serious: integer | null;
    };
    primary_cta_above_fold: boolean | null;
    mobile_overflow: boolean | null;
  };
  not_measured: string[];
  sections: {
    i: integer;
    kind: string;
    heading?: string | null;
    tactics?: string[];
  }[];
  omitted?: {
    present: integer;
    missing: integer;
    sections: integer;
  };
  annotation?: "llm" | "detectors_only";
}
```

**Site audit ready**

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  kind: "site_audit";
  url: string;
  usage: {
    audits_used: integer;
    audits_limit: integer;
    period_end: string;
  };
  next: string;
  status: "ready";
  pages: {
    audit_id: string /* uuid */;
    url: string;
    status: "queued" | "running" | "ready" | "failed" | "cancelled";
    page_type: string | null;
    present_count: integer;
    missing_count: integer;
  }[];
}
```

**Verification ready**

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  kind: "verify";
  url: string;
  usage: {
    audits_used: integer;
    audits_limit: integer;
    period_end: string;
  };
  next: string;
  status: "ready";
  verification: {
    status: "verified_present" | "verified_absent" | "regressed" | "no_baseline" | "undetermined";
    tactic_id: string /* uuid */;
    tactic_slug: string;
    site_change_id: string /* uuid */;
    present_before: boolean | null;
    present_after: boolean | null;
    confidence: number | null;
    detection: "detector" | "llm" | "undetermined";
    regressions: {
      check: string;
      before: number | boolean | null;
      after: number | boolean | null;
      limit: string;
    }[];
    checks: {
      check: "accessibility" | "lcp" | "cls" | "mobile_overflow" | "cta_count";
      status: "pass" | "fail" | "not_measured";
      before: number | boolean | null;
      after: number | boolean | null;
      limit: string;
    }[];
    dom_changed: boolean | null;
    sections: {
      added: string[];
      removed: string[];
      changed: string[];
    } | null;
    change_summary: string;
    before_captured_at: string | null;
    after_captured_at: string;
    before_image_url: string | null;
    after_image_url: string | null;
    image_expires_in: integer | null;
    basics_after: {
      lcp_ms: number | null;
      cls: number | null;
      axe: {
        critical: integer | null;
        serious: integer | null;
      };
      primary_cta_above_fold: boolean | null;
      mobile_overflow: boolean | null;
    };
    annotation: "llm" | "detectors_only";
  };
}
```

## recommend

**Recommend changes for an audited page.** Turn a page audit into a ranked implementation plan: each item names a tactic, its evidence grade, effort, where it goes on the page, its structure, copy patterns and acceptance criteria. Call after audit_page (it also works while the audit is still running). The first call starts generating the plan and returns status "generating" with no items; call again after poll_after_seconds. Next: for each item, call get_tactic (and get_examples for visual references) before editing code, implement the change in the codebase, then call verify_change once it is deployed. Never promise a numeric conversion lift unless an item's grade is "measured".

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `audit_id` | string (uuid) | yes | A page audit_id from audit_page or from a site audit's pages |
| `appetite` | "hours" \\| "days" \\| "weeks" | no | How much effort the team can spend |
| `goal` | string | no | max 500 chars; The business goal, e.g. "more trial signups" |
| `max_items` | integer | no | 1–8 |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  status: "generating" | "ready";
  poll_after_seconds: integer | null;
  appetite: "hours" | "days" | "weeks" | null;
  goal: string | null;
  items: {
    rank: integer;
    tactic_id: string /* uuid */;
    slug: string;
    title: string;
    grade: "observed" | "tested" | "measured";
    effort: "hours" | "days" | "weeks";
    bucket: "quick_win" | "strategic_bet" | "delight_layer";
    rationale: string;
    where: string;
    structure: string;
    copy_patterns: string[];
    acceptance_criteria: string[];
    example_ids: Array<string /* uuid */>;
  }[];
  upgrade_hint?: string;
  next: string;
}
```

## search_tactics

**Search CRO tactics.** Semantic search over the published tactic library (patterns observed on top SaaS sites, each with an evidence grade). Use it when the user asks about a specific idea ("social proof near pricing", "shorter signup form") or when you need tactics beyond those an audit flagged. Grades: observed (seen on many sites) < tested (experiment evidence) < measured (outcome data); filter with min_grade. Next: call get_tactic on a result for its implementation spec, and get_examples for references.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `query` | string | yes | max 500 chars |
| `page_type` | "homepage" \\| "pricing" \\| "signup" \\| "demo" \\| "contact" \\| "product" \\| "customers" \\| "about" \\| "comparison" \\| "landing" \\| "other" | no |  |
| `vertical` | string | no | max 50 chars |
| `min_grade` | "observed" \\| "tested" \\| "measured" | no |  |
| `limit` | integer | no | 1–10 |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  query: string;
  results: {
    tactic_id: string /* uuid */;
    slug: string;
    name: string;
    category: "messaging" | "hero" | "cta" | "navigation" | "pricing" | "social_proof" | "forms" | "funnel" | "product_imagery" | "personas" | "comparison" | "trust" | "footer" | "about" | "other";
    grade: "observed" | "tested" | "measured";
    page_types: string[];
    similarity: number;
    mechanism: string;
  }[];
  next: string;
}
```

## get_tactic

**Get a tactic.** Full detail for one tactic: the mechanism (why it works), how to detect it, the implementation spec (where on the page, structure, copy patterns, acceptance criteria), anti-patterns, evidence links and how many corpus sites use it. Pass tactic_id (from recommend, get_audit or search_tactics) or slug. Call it before implementing any tactic, and use the acceptance criteria to check your own change. Next: get_examples for visual references, then implement, then verify_change.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `tactic_id` | string (uuid) | no |  |
| `slug` | string | no | max 100 chars |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  id: string /* uuid */;
  slug: string;
  name: string;
  category: "messaging" | "hero" | "cta" | "navigation" | "pricing" | "social_proof" | "forms" | "funnel" | "product_imagery" | "personas" | "comparison" | "trust" | "footer" | "about" | "other";
  grade: "observed" | "tested" | "measured";
  page_types: string[];
  mechanism: string;
  detection_criteria: string;
  implementation_spec: {
    where: string;
    structure: string;
    copy_patterns?: string[];
    components?: string[];
    acceptance_criteria: string[];
  };
  anti_patterns: string[];
  evidence: {
    kind: "experiment" | "case_study" | "benchmark" | "article";
    title: string | null;
    url: string;
    direction: "supports" | "contradicts" | "mixed" | null;
    date: string | null;
  }[];
  observed_on: integer;
  updated_at: string;
  upgrade_hint?: string;
  next: string;
}
```

## get_examples

**Get real-world examples of a tactic.** Published examples of a tactic from named sites: a cropped screenshot of the section (image_url, a signed link valid for image_expires_in seconds), the copy used and commentary. Use them as visual and copy references while implementing; adapt, never copy another site's text verbatim. include_counter_examples adds examples of the tactic done badly. Fetch image_url promptly; call get_examples again for fresh links after they expire.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `tactic_id` | string (uuid) | yes |  |
| `vertical` | string | no | max 50 chars |
| `limit` | integer | no | 1–5 |
| `include_counter_examples` | boolean | no |  |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  tactic_id: string /* uuid */;
  examples: {
    example_id: string /* uuid */;
    site: string;
    page_type: string | null;
    section_kind: string | null;
    captured_at: string | null;
    copy_excerpt: string | null;
    commentary: string;
    is_counter_example: boolean;
    image_url: string | null;
    image_expires_in: integer | null;
  }[];
  upgrade_hint?: string;
  next: string;
}
```

## verify_change

**Verify a shipped change.** After you have implemented a tactic and the change is live on the public URL, call this to re-capture the page, check whether the tactic is now present, and run five pass/fail regression gates against the page as it was before: accessibility (axe critical+serious must not increase), LCP ≤ max(before × 1.2, 2500 ms), CLS ≤ max(before + 0.05, 0.1), no horizontal overflow at 390 px, and CTA count must not drop. The baseline is the latest audit_page capture of this URL; if none exists the result is status "no_baseline" with no before state and no comparison, so run audit_page before implementing. Do not call it before the change is deployed. Every call re-captures the page (it does not count against the audit allowance). Returns an audit_id immediately. Next: poll get_audit with the audit_id until status is "ready", then report the verification to the user. Never claim a numeric lift unless get_outcomes reports a measured result.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `url` | string (url) | yes | max 2048 chars; The live URL where the change was deployed |
| `tactic_id` | string (uuid) | yes | The tactic you implemented (from recommend or get_tactic) |
| `site_change_id` | string (uuid) | no | An existing change record to update, from site_memory |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  audit_id: string /* uuid */;
  kind: "page_audit" | "site_audit" | "verify";
  status: "queued" | "running" | "ready" | "failed" | "cancelled";
  url: string;
  poll_after_seconds: integer;
  reused: boolean;
  max_pages?: integer;
  tactic_id?: string /* uuid */;
  usage: {
    audits_used: integer;
    audits_limit: integer;
    period_end: string;
  };
  next: string;
}
```

## connect_analytics

**Connect analytics.** Get a one-time link the human opens in a browser to connect Google Analytics 4 or Search Console (Google sign-in) or Clarity (project API token form) for a site, so verified changes can be measured. You never see or handle analytics credentials: give the link to the user and ask them to open it. The link works once and expires after expires_in seconds (15 minutes). Next: once connected, the outcomes job records before/after metrics for each verified change after it has been live 28 days (plus about 3 days of data lag); get_outcomes reports them. Clarity keeps only a few days of history, so it is stored but not used for 28-day outcomes.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `url` | string (url) | yes | max 2048 chars; Any URL on the site |
| `provider` | "ga4" \\| "gsc" \\| "clarity" | yes |  |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  provider: "ga4" | "gsc" | "clarity";
  host: string;
  link: string;
  expires_in: integer;
  next: string;
}
```

## get_outcomes

**Get measured outcomes.** Analytics outcomes for verified changes on a site: each metric for the 28 days before and the 28 days after the change, with both periods and their session counts. comparison.relative_change is filled only when BOTH periods have at least 500 sessions (label "measured"); otherwise it is null and you report the two values side by side with their sample sizes. It is a before/after comparison, not a controlled experiment. Outcomes appear only after a verified change has been live 28 days plus about 3 days of data lag and the outcomes job has measured it, so recent changes show nothing yet. If nothing is connected the result is empty with a note; changes then stay "shipped, unmeasured". Never compute or claim a lift yourself.

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `url` | string (url) | yes | max 2048 chars; Any URL on the site |
| `since` | string | no | Only outcomes whose period ends on or after this date (YYYY-MM-DD) |

### Output

```ts
{
  corpus_version: string | null;
  tactic_count: integer;
  host: string;
  outcomes: {
    site_change_id: string /* uuid */;
    tactic_id: string | null;
    slug: string | null;
    provider: "ga4" | "gsc" | "clarity";
    metric_name: string;
    baseline_value: number | null;
    outcome_value: number | null;
    period_start: string;
    period_end: string;
    sample_size: integer | null;
    baseline_period: {
      start: string;
      end: string;
    };
    outcome_period: {
      start: string;
      end: string;
    };
    baseline_sample_size: integer | null;
    outcome_sample_size: integer | null;
    comparison: {
      relative_change: number | null;
      label: "measured" | "insufficient_sample" | "not_comparable";
      reason: string;
    };
  }[];
  note: string | null;
  next: string;
}
```

## Prompts

The server also offers prompts that walk an agent through the loop:

- `optimise_pricing_page`: Audit a pricing page, plan evidence-graded changes, implement them in this codebase and verify them live.
- `optimise_signup_flow`: Audit a signup page, plan friction-reducing changes, implement them and verify them live.
- `audit_site_and_plan`: Audit a site's key pages and produce a prioritised plan, then implement and verify the top items.
