Getting started

Quickstart

Publish your first widget and push live data to it in a few minutes.

This walks through publishing a widget and pushing data to it. You’ll use Claude Code (which drives perch-mcp), but the same calls work from any MCP client.

Before you start

  • The Perch Mac app is running and shows an iPhone as paired + connected.
  • Claude Code is registered with the bundled perch-mcp server (the Mac app does this for you on first launch).

You can confirm the link anytime with the pair_status tool.

1. Write the widget HTML

A widget is one self-contained HTML document. It reads data through window.PERCH.onData and renders it — nothing more.

<!doctype html>
<html lang="en">
  <head><meta charset="utf-8" /></head>
  <body class="perch-glow">
    <div class="perch-safe">
      <div class="perch-content" style="display:grid;place-items:center">
        <div id="temp" style="font-size:20vmin;font-weight:800;color:var(--perch-ink)">—</div>
      </div>
    </div>
    <script>
      window.PERCH.onData((d) => {
        document.getElementById("temp").textContent =
          (d && typeof d.tempC === "number") ? Math.round(d.tempC) + "°" : "—";
      });
      window.PERCH.ready();
    </script>
  </body>
</html>

Note what’s absent: no fetch, no API key, no polling loop. The classes perch-safe, perch-content, and perch-glow, and the --perch-* CSS variables, are provided by the runtime — see Theming.

2. Publish it

Ask Claude Code, in plain language:

“Publish a widget called Temperature with this HTML, then make it the active widget.”

Under the hood that calls the publish_widget tool and then set_active_widget:

// publish_widget
{ "name": "Temperature", "html": "<!doctype html>…", "refreshMs": 1500 }
// → { "id": "wgt_abc123" }

The phone switches to your widget immediately. It shows until data arrives.

3. Push data

// push_data
{ "id": "wgt_abc123", "data": { "tempC": 21.4 } }

The phone picks this up on its next poll and your onData callback runs with { tempC: 21.4 }. Push again with a new value and the display updates live — the page never reloads.

4. Update from a script (optional)

To feed a widget from a cron job or external script instead of the MCP, get its authenticated endpoint with get_data_endpoint:

// get_data_endpoint → { "url": "https://…/admin/data/wgt_abc123", "token": "…" }
curl -X POST "$URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tempC": 22.0}'

That’s the whole loop: publish HTML → push JSON → it’s live. From here, learn the full widget anatomy and the window.PERCH API.