Surfaces lets agents update iOS home-screen widgets by sending a validated JSON document of semantic content blocks. The iOS app owns layout, theme, and rendering so agents send meaning instead of visual instructions.
In Surfaces, open Connect and copy the remote MCP URL. It has this shape:
https://surfaces.app/mcp/<connection-token>
Add that URL as a remote MCP server in your agent. The visible app text hides the secret token; Copy includes the full URL.
{
"version": 3,
"blocks": [
{
"block": "heading",
"id": "hd-today",
"text": "Today"
},
{
"block": "task",
"id": "reply-sam",
"text": "Reply to Sam"
}
]
}
| Block | Trait | Unit Cost | Required Props | Optional Props |
|---|---|---|---|---|
heading |
section | 1 | text: string, <= 40 | id: string, <= 64, [A-Za-z0-9._-]; detail: string, <= 24 |
text |
row | 1-3 measured, small caps at 2 | text: string, <= 280 | id: string, <= 64, [A-Za-z0-9._-] |
task |
row | 1-2 measured | text: string, <= 120 | id: string, <= 64, [A-Za-z0-9._-]; state: enum, open | done, default open |
event |
row | 2 | title: string, <= 80; start: string, iso8601 | id: string, <= 64, [A-Za-z0-9._-]; end: string, iso8601; location: string, <= 40 |
metric |
tile | 2 | label: string, <= 24; value: string, <= 16 | id: string, <= 64, [A-Za-z0-9._-]; accent: enum, mint | coral | sky | amber | lavender | rose |
status |
tile | 2 | label: string, <= 24; level: enum, ok | warn | down | id: string, <= 64, [A-Za-z0-9._-]; detail: string, <= 24 |
{
"version": 3,
"blocks": [
{
"block": "heading",
"id": "today",
"text": "Morning brief"
},
{
"block": "task",
"id": "reply-sam",
"text": "Reply to Sam"
},
{
"block": "task",
"id": "invoice",
"text": "Send invoice"
},
{
"block": "event",
"id": "standup",
"title": "Standup",
"start": "2026-07-04T14:30:00Z"
}
]
}
{
"version": 3,
"blocks": [
{
"block": "heading",
"id": "project",
"text": "Project status"
},
{
"block": "status",
"id": "api",
"label": "API",
"level": "ok",
"detail": "142ms"
},
{
"block": "status",
"id": "ios",
"label": "iOS",
"level": "warn",
"detail": "review"
},
{
"block": "metric",
"id": "focus",
"label": "Focus",
"value": "2h 14m"
},
{
"block": "task",
"id": "ship",
"text": "Ship Phase 4"
}
]
}
{
"version": 3,
"blocks": [
{
"block": "heading",
"id": "inbox",
"text": "Inbox triage"
},
{
"block": "task",
"id": "reply-lee",
"text": "Reply to Lee"
},
{
"block": "task",
"id": "approve-copy",
"text": "Approve launch copy"
},
{
"block": "text",
"id": "waiting",
"text": "Waiting on finance for the updated invoice."
},
{
"block": "event",
"id": "sync",
"title": "Client sync",
"start": "2026-07-04T17:00:00Z"
}
]
}
curl -X POST "$OPENWIDGET_BASE_URL/v1/s/$OPENWIDGET_WRITE_TOKEN" \
-H "content-type: application/json" \
--data @widget.json
curl -X POST "$OPENWIDGET_BASE_URL/v1/validate" \
-H "content-type: application/json" \
--data @widget.json
curl "$OPENWIDGET_BASE_URL/v1/s/$OPENWIDGET_READ_TOKEN"
{
"mcpServers": {
"openwidget": {
"command": "npx",
"args": [
"openwidget-mcp"
],
"env": {
"OPENWIDGET_BASE_URL": "OPENWIDGET_BASE_URL",
"OPENWIDGET_CONNECTION_TOKEN": "OPENWIDGET_CONNECTION_TOKEN"
}
}
}
}