Surfaces v3

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.

Remote MCP

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.

Document Shape

{
  "version": 3,
  "blocks": [
    {
      "block": "heading",
      "id": "hd-today",
      "text": "Today"
    },
    {
      "block": "task",
      "id": "reply-sam",
      "text": "Reply to Sam"
    }
  ]
}

Block Reference

BlockTraitUnit CostRequired PropsOptional 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

Recipes

morning brief

{
  "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"
    }
  ]
}

project status

{
  "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"
    }
  ]
}

inbox triage

{
  "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

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"

Advanced stdio MCP Config

{
  "mcpServers": {
    "openwidget": {
      "command": "npx",
      "args": [
        "openwidget-mcp"
      ],
      "env": {
        "OPENWIDGET_BASE_URL": "OPENWIDGET_BASE_URL",
        "OPENWIDGET_CONNECTION_TOKEN": "OPENWIDGET_CONNECTION_TOKEN"
      }
    }
  }
}