LVNDRby Toolfora

HelpDevelopers

Built for stores run by agents

LVNDR records changes to a Shopify store's products, collections, pages, blogs and posts, menus, metafield definitions and theme settings, with who made them as far as Shopify says, and puts them back in one call (themes excepted). Connect your agent over MCP and it can announce what it is about to do, check its own work, and undo it when the result is wrong.

Works with Claude Code, Claude Desktop, Cursor, VS Code, Codex, the lvndr CLI and plain HTTPS. Free on development stores.

The safe workflow

  1. Install LVNDR on the store

    Install the LVNDR app on the Shopify store and wait for its first snapshot (the change log shows the progress). Then connect your agent below and call lvndr_status to confirm which store it reached.

  2. Start a run with a restore point

    lvndr_start_run with the agent name and the task. LVNDR marks a restore point and tags the changes that follow with the run. Which changes depends on the key: the answer's capture says so.

  3. Act with your Shopify tool

    Through the Shopify admin, the Admin API, a CLI or any other tool. LVNDR watches the store; nothing goes through it.

  4. End the run and wait for LVNDR to catch up

    lvndr_end_run closes it. Changes are tagged within about 4 minutes (collection rules and SEO, menus, themes and metafield definitions up to 10). Poll lvndr_get_run about every 20 seconds until attributedThrough is after your last write and pendingDeliveries is 0, then review what the run touched.

  5. Preview, then undo

    If the result is wrong, lvndr_preview_undo_run shows the plan and returns a confirmation token; lvndr_undo_run with the token puts every change back, newest first. lvndr_preview_undo and lvndr_undo do the same for one change. The merchant sees the same run in the app with the same buttons.

The whole workflow, with what LVNDR cannot undo, is in Running agents safely.

Connect your agent

The MCP endpoint is https://lvndr.toolfora.com/mcp. Clients that support MCP authorization need only that URL: the client opens a browser, the merchant names the store, approves inside their Shopify admin, and the client receives its own key: one hour, refreshed automatically, listed in Settings > Agents & API as “Connected via OAuth” and revocable there. Details in MCP.

The second option, for clients without OAuth or for scripts, is an API key from the app: Settings > Agents & API > Create key. Replace lv_YOUR_KEY in the key snippets with it, or set it as LVNDR_API_KEY where the snippet says so.

Claude Code

Keyless (OAuth): add the server, then run /mcp in Claude Code to sign in

claude mcp add -t http lvndr https://lvndr.toolfora.com/mcp

With an API key

claude mcp add -t http -H "Authorization: Bearer lv_YOUR_KEY" lvndr https://lvndr.toolfora.com/mcp

Or a project .mcp.json with the key from LVNDR_API_KEY (commit it)

{
  "mcpServers": {
    "lvndr": {
      "type": "http",
      "url": "https://lvndr.toolfora.com/mcp",
      "headers": {
        "Authorization": "Bearer ${LVNDR_API_KEY}"
      }
    }
  }
}

Cursor

Keyless (OAuth): .cursor/mcp.json or ~/.cursor/mcp.json; Cursor offers to log in

{
  "mcpServers": {
    "lvndr": {
      "url": "https://lvndr.toolfora.com/mcp"
    }
  }
}

Add to Cursor with a keySet LVNDR_API_KEY in your shell before starting Cursor.

Or .cursor/mcp.json with a key

{
  "mcpServers": {
    "lvndr": {
      "url": "https://lvndr.toolfora.com/mcp",
      "headers": {
        "Authorization": "Bearer lv_YOUR_KEY"
      }
    }
  }
}

VS Code

Keyless (OAuth): .vscode/mcp.json; VS Code runs the sign-in

{
  "servers": {
    "lvndr": {
      "type": "http",
      "url": "https://lvndr.toolfora.com/mcp"
    }
  }
}

Add to VS Code with a keyVS Code asks for the key the first time the server starts.

Or .vscode/mcp.json with a key

{
  "inputs": [
    {
      "type": "promptString",
      "id": "lvndr-key",
      "description": "LVNDR API key",
      "password": true
    }
  ],
  "servers": {
    "lvndr": {
      "type": "http",
      "url": "https://lvndr.toolfora.com/mcp",
      "headers": {
        "Authorization": "Bearer ${input:lvndr-key}"
      }
    }
  }
}

Codex

~/.codex/config.toml

[mcp_servers.lvndr]
url = "https://lvndr.toolfora.com/mcp"
bearer_token_env_var = "LVNDR_API_KEY"

Export LVNDR_API_KEY before starting Codex.

Claude Desktop

claude_desktop_config.json, via mcp-remote

{
  "mcpServers": {
    "lvndr": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://lvndr.toolfora.com/mcp",
        "--header",
        "Authorization: Bearer lv_YOUR_KEY"
      ]
    }
  }
}

Settings > Developer > Edit Config, then restart Claude Desktop.

CLI

Environment

export LVNDR_API_KEY=lv_YOUR_KEY
export LVNDR_API_URL=https://lvndr.toolfora.com

Then

npx lvndr status
npx lvndr changes --since 24h

Every command is listed in the CLI article.

REST

The newest five changes

curl -H "Authorization: Bearer lv_YOUR_KEY" https://lvndr.toolfora.com/api/v1/changes?limit=5

MCP tools

One Streamable HTTP endpoint, https://lvndr.toolfora.com/mcp, with an OAuth token or a key as the bearer. Results come back as text and as structuredContent. A tool that writes to the store needs a confirmation_token from its preview, for every kind of key: valid 10 minutes, single use, and refused if the store changed since the preview. Over the REST API the same holds for the dry run's planHash, and in the CLI for --plan-hash. The record is snapshotted first, so an undo can itself be undone.

Read

ToolScopeWhat it does
lvndr_statusreadPlan, tracked counts, last activity check and pipeline health.
lvndr_search_changesreadChanges newest first, filtered by time, type, record, actor, run or text.
lvndr_get_changereadOne change with its full before/after diff.
lvndr_record_historyreadEvery version and change of one record, newest first.
lvndr_get_batchreadEverything the same actor did around a change (no gap over ten minutes).
lvndr_list_restore_pointsreadThe moments the store can be rolled back to.
lvndr_preview_undoreadWhat undoing one change would write, field by field. Writes nothing; returns the confirmation_token for lvndr_undo.
lvndr_preview_undo_runreadWhat undoing a whole agent run would do, change by change. Writes nothing; returns the confirmation_token for lvndr_undo_run.
lvndr_preview_undo_batchreadWhat undoing one actor's batch would do. Writes nothing; returns the confirmation_token for lvndr_undo_batch.
lvndr_preview_rollbackreadWhat rolling back to a restore point would undo. Writes nothing; returns the confirmation_token for lvndr_rollback.
lvndr_list_alertsreadAnomaly alerts, open ones by default: bursts of changes by one actor.
lvndr_get_runreadOne agent run: who, what task, its restore point, its changes, and attributedThrough / pendingDeliveries to tell whether LVNDR has caught up.
lvndr_list_runsreadRecent agent runs, newest first; openOnly for runs left open.
lvndr_get_jobreadProgress of a run undo, batch undo or rollback job.

Write

ToolScopeWhat it does
lvndr_mark_restore_pointreadMarks a moment to roll back to. Writes only to LVNDR.
lvndr_start_runreadAnnounces a run: a restore point is marked and changes until the end are tagged with it. With the undo scope the run captures every unattributed change; without it, only the key's own OAuth client's changes, or none.
lvndr_end_runreadCloses the run and reports how many changes it made.
lvndr_undoundoPuts one change back in the live store. Needs the confirmation_token from lvndr_preview_undo.
lvndr_undo_runundoUndoes every change of a run, newest first, as a background job. Needs the confirmation_token from lvndr_preview_undo_run.
lvndr_undo_batchundoUndoes a whole batch, newest first, as a background job. Needs the confirmation_token from lvndr_preview_undo_batch.
lvndr_rollbackrollbackUndoes everything since a restore point, newest first. Needs the confirmation_token from lvndr_preview_rollback.
lvndr_dismiss_alertundoMarks an alert as seen. Writes only to LVNDR, but needs Undo so a read-only key cannot silence its own alert.

Key scopes

A key carries the scopes it was made with. Give an agent the least it needs; make another key when it needs more.

ScopeAllows
readEvery read tool and route, previews, marking restore points, starting and ending runs, dismissing alerts, on MCP, REST and the CLI alike. Always on.
undoUndoing one change, a run or a batch. A run started by a key with this scope captures every unattributed change while it is open; without it, a run captures only the key's own OAuth client's changes, or none.
rollbackRolling the whole store back to a restore point.
exportDownloading the change log as CSV or JSON.

A key made in Settings expires in 30, 90 (the default) or 365 days, or never, and can be revoked there at any time. Every write an agent makes through LVNDR is attributed to its key in the change log and the audit log. For changes an agent makes directly in the store, Shopify names the app only for lifecycle actions such as creating, publishing or deleting a record; field edits are inferred and labelled as such. The merchant can mark an app title or client id as an agent so it trips anomaly alerts sooner.

Rate limits

Per key, by the store's plan. Until managed billing is on, every store gets the Scale numbers.

PlanReads per minuteWrites per minute
Starter6010
Growth12020
Scale30060
Development store (free)12020

A request over the limit gets 429 with a Retry-After header. Over MCP, an OAuth token gets the same 429; a key made in Settings gets a tool result saying “Rate limited; retry in N s”. A path that does not exist costs nothing. Writes send an Idempotency-Key header to make a retry safe.

Alert webhook

Set a webhook URL in Settings > Alert webhook and LVNDR POSTs every anomaly alert to it as JSON, alongside the in-app banner and the email. Each request carries X-Lvndr-Signature: sha256=<hex>, the HMAC-SHA256 of the raw body with your webhook secret. Verify it before trusting the payload:

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(header ?? "");
  return a.length === b.length && timingSafeEqual(a, b);
}
What LVNDR cannot do
  • Themes are tracked but never written: Shopify grants theme writes to a short list of apps. The undo preview shows the earlier value for a manual restore.
  • Orders and customers are never read: they are protected data and out of scope by design.
  • Field-level edits carry no staff name in Shopify's own records, so those actors are inferred, and labelled as such.
  • Media is restored by reference or re-uploaded from its URL; inventory history is per location and quantity only.
  • A deleted record comes back with a new id. A rebuilt product has no media, inventory or sales channels; a rebuilt blog has no posts.

More in the help articles: Running agents safely, MCP, REST API, CLI, what is tracked.