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-mcpserver (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.