API

Manifest & result types

Widget metadata, icons, hub response shapes, and MCP arguments.

Every widget has a manifest — the metadata the hub stores alongside its HTML. You don’t write it directly; the hub builds it when you publish. But knowing the fields helps when you list widgets or debug versioning. This is the current hub record for a published widget, not a portable template package or a declaration of which rendering surfaces it supports.

Manifest fields

FieldTypeMeaning
idstringStable widget id (returned by publish_widget).
namestringHuman-readable name (switcher + Mac list).
refreshMsintStored refresh metadata, default 1500. The current iOS app does not use it to schedule its SSE stream or roughly 10-second backup poll.
htmlHashstringSHA-256 (hex) of the HTML, for integrity.
versionintBumped on every update so the phone knows to refetch.
publishedAtdouble?First-published time (Unix seconds). Optional.
iconWidgetIcon?Switcher / Mac list / Lock Screen icon. Optional.
builtInSlugstring?If installed from a shipped template, its stable slug (e.g. "soundbar"). nil for custom widgets.
builtInVersionint?The built-in template version it was installed from.
templateHashstring?SHA-256 of the pristine built-in or registry HTML last installed. htmlHash != templateHash means it has local HTML edits; upstream update checks compare against this hash.
sourcestring?Publishing channel: builtin, mcp, cli, or registry.
registryIdstring?UUID of the registry widget this copy was installed from.
registryVersionint?Registry version this copy was installed from.
publishedRegistryIdstring?UUID of the registry entry this local widget publishes to; separate from its installation source.

list_widgets returns an array of these plus the activeWidgetId.

The registry also carries an envelopeHash for complete envelope integrity. Current update offers compare HTML hashes, so an icon-only or sample-data-only registry revision does not by itself trigger an update offer.

Icons

A WidgetIcon has a kind and a value:

{ "kind": "sfSymbol", "value": "thermometer" }
{ "kind": "emoji",    "value": "🎵" }
{ "kind": "png",      "value": "<base64-encoded PNG bytes>" }
KindvalueNotes
sfSymbolSF Symbol nameBest default — crisp, tint-safe, desaturates cleanly on the Lock Screen.
emojione emojiColorful in-app; flattens to a tinted silhouette on the Lock Screen.
pngbase64 PNG on input~256px, transparent background, monochrome silhouette, ≤256 KiB.

For a png, the hub stores the bytes, computes their SHA-256, and rewrites value to that hash (a cache key); the raw bytes are served at GET /widget/:id/icon. So a manifest you read back will show the hash, not the base64 you sent.

Set or change an icon without touching the HTML via set_widget_icon:

{ "id": "wgt_abc123", "icon": { "kind": "sfSymbol", "value": "thermometer.sun" } }

Formal shapes

The typed shapes behind the tables above and the hub’s JSON responses. MCP tool calls wrap their output in text content: some include serialized JSON, while others describe the result in prose. See MCP results.

WidgetManifest

interface WidgetManifest {
  id: string;
  name: string;
  refreshMs: number;
  htmlHash: string;        // SHA-256 (hex) of the HTML
  version: number;
  publishedAt?: number;    // Unix seconds
  icon?: WidgetIcon;
  builtInSlug?: string;    // set if installed from a shipped template
  builtInVersion?: number;
  templateHash?: string;   // SHA-256 of the pristine template last installed
  source?: "builtin" | "mcp" | "cli" | "registry";
  registryId?: string;
  registryVersion?: number;
  publishedRegistryId?: string;
}

WidgetIcon

interface WidgetIcon {
  kind: "sfSymbol" | "emoji" | "png";
  // sfSymbol: symbol name · emoji: the character ·
  // png: base64 PNG on input, rewritten to the bytes' SHA-256 once stored
  value: string;
}

WidgetVersion

One entry in a widget’s bounded code history. list_versions returns these newest-first; set_widget_version selects one as the live version (no new entry is appended — isCurrent moves to it and history stays intact).

interface WidgetVersion {
  version: number;
  htmlHash: string;        // SHA-256 (hex) of that snapshot's HTML
  updatedAt: number;       // Unix seconds
  note?: string;
  isCurrent: boolean;      // the version currently live (selection moves this)
  builtInVersion?: number; // set when this cut is a pristine copy of a shipped
                           // built-in template (labels stock cuts vs your edits)
}
interface ListVersionsResult { versions: WidgetVersion[]; }

Hub result objects

// GET /admin/pair-status (the MCP's pair_status summarizes this in text)
interface PairStatus {
  paired: boolean;
  connected: boolean;
  deviceName?: string;
  deviceKind?: string;     // e.g. "iphone" or "ipad"
}

// POST /admin/publish (the MCP returns a message containing the id)
interface PublishWidgetResult { id: string; }

// list_widgets
interface ListWidgetsResult { widgets: WidgetManifest[]; activeWidgetId?: string; }

// list_versions → ListVersionsResult (see WidgetVersion above)

// GET /admin/data-endpoint/:id (the MCP formats the URL and auth header as text)
interface DataEndpoint { url: string; token: string; }

Tool arguments at a glance

ToolRequiredOptional
publish_widgetname, htmlrefreshMs, icon, actions
update_widgetid, htmlicon, refreshMs, note
set_widget_iconid, icon—
set_active_widgetid—
delete_widgetid—
list_versionsid—
set_widget_versionid, version—
push_dataid, data—
get_data_endpointid—
register_actionwidgetId, actionId, capabilitydescription, argKeys, requiresConfirmation

requiresConfirmation is currently stored metadata; the phone does not enforce per-invocation confirmation. Action consent is enforced separately on the Mac; see Interactive actions.