В конце марта 2026 года Figma открыла канвас для AI‑агентов. Claude Code, Codex и подобные инструменты получили возможность создавать и редактировать макеты через MCP‑сервер Figma.

Я решил проверить, насколько хорошо Claude Code сможет перенести дизайн сайтов в макет и сколько это будет стоить. Для этого я выбрал несколько лендингов и попросил агента перенести их на канвас — пробовал давать ему либо скриншот, либо ссылку на сайт. Каждый раз я смотрел, какие шаги выполняет агент, сколько раз обращается к инструментам и сколько токенов (то есть денег) тратит.

При взаимодействии с Figma MCP агент фактически собирает макет с помощью JavaScript. Claude Code генерирует код, который через Figma Plugin API создаёт фреймы и слои, задаёт им размеры, координаты, цвета, шрифты и другие свойства.

Флоу работы ии агента с Figma MCP
Флоу работы ии агента с Figma MCP

Как проходил эксперимент

Я выбрал три страницы:

Для сайтов Hermes и PostHog в промпт агенту я передавал скриншот страниц. Так же в контекст агента передавалась папка с картинками, логотипами и другими ресурсами, которые были на сайте. Для Tailwind я предоставил агенту только ссылку на сайт: агент должен был сам изучить HTML и CSS, найти нужные ассеты и снять страницу в браузере.

Я использовал модель Opus 5 с effort на уровне high. Для каждой сессии с Claude Code я создавал чистый harness — без дополнительных скиллов, памяти и плагинов. Таким образом агент мог использовать только MCP Figma за сессию.

После каждого прогона агента я считал:

  • стоимость всей сессии

  • время работы

  • количество обращений к инструментам

  • стоимость MCP‑вызовов

  • расходы на анализ и промежуточные проверки

  • визуальное сходство с оригиналом

После запуска агента я не вмешивался в его работу и ждал пока дизайн будет полностью готов.

Промпты для агента

Для первых двух сайтов — создание дизайна по изображению:

Recreate this landing page section as a design: <Image>. Make exactly one frame of the landing page via Figma MCP. Use the assets folder to get the exact illustrations from the page and place them as is: you may only change their size — no CSS filters, blend modes, recoloring, opacity or cropping. Work in this Figma file: <URL

Для последнего сайта — создание дизайна по ссылке:

Recreate the landing page at <SITE‑URL> as a design — the first screen as it looks in the browser at 1440px wide. Make exactly one frame via Figma MCP. Use the real logos and images from the site. Work in this Figma file: <URL>

Результаты

По итогу работы трёх сессий с Figma получилось следующее:

Общая стоимость

$34,21

Время

98,5 минуты

Токены

40,6 млн

Если погружаться в детализацию, то можно выделить следующие этапы работы агента и затраты на каждый из этапов:

Этап

Стоимость

Анализ до работы с канвасом

$9,10

MCP‑вызовы

$14,96

Работа вокруг MCP

$10,15

Работа с канвасом: MCP‑вызовы + работа вокруг MCP

$25,11

Вся сессия

$34,21

  1. Анализ до канваса — все ответы до того момента, когда агент начал работать с документом Figma

  2. MCP‑вызовы — ответы, в которых агент вызывал MCP‑инструмент, а также следующие ответы, в которых он получал результат этого вызова

  3. Работа вокруг MCP — остальные действия после начала сборки макета: команды Bash, чтение файлов, скачивание и сравнение скриншотов

Получившиеся макеты
Сравнение оригинала HermesAgent с получившимся макетом
Сравнение оригинала HermesAgent с получившимся макетом
Сравнение оригинала PostHog с получившимся макетом
Сравнение оригинала PostHog с получившимся макетом
Сравнение оригинала Tailwind CSS с получившимся макетом
Сравнение оригинала Tailwind CSS с получившимся макетом

Как я считал токены и стоимость

После каждой сессии Claude Code сохраняет лог в формате JSONL. В нем записаны сообщения агента, вызовы инструментов и поле usage для каждого ответа модели. Из usage я брал четыре значения:

  • input_tokens — новые входные токены

  • output_tokens — ответ модели, включая код и аргументы инструментов

  • cache_read_input_tokens — контекст, прочитанный из кэша

  • cache_creation_input_tokens — контекст, записанный в кэш

В строке «Токены» я складывал все четыре значения. Для подсчёта стоимости использовал тариф Opus 5 с часовым кэшем: $5 за миллион входных токенов, $25 за миллион выходных, $0,50 за чтение из кэша и $10 за запись в кэш.

Вот основная часть скрипта, которая переводит токены одного ответа в доллары:

PRICE = {
    "input_tokens": 5.0,
    "output_tokens": 25.0,
    "cache_read_input_tokens": 0.5,
    "cache_creation_input_tokens": 10.0,
}

def cost(usage):
    return sum(
        usage.get(token_type, 0) * price
        for token_type, price in PRICE.items()
    ) / 1_000_000

Разберем детально каждый шаг на пути агента при выполнении этой задачи.

Что Claude Code делает до сборки дизайна

Перед сборкой макета агент не начинает с создания элементов. Сначала он исследует фото или ссылку на сайт: размеры блоков, координаты, цвета, шрифты и правила обрезки изображений. По журналам трёх сессий я обнаружил, что способ анализа сильно зависит от входных данных.

Если на входе только скриншот

В тестах с Hermes и PostHog у агента был только скриншот страницы. Поэтому он исследовал изображение примерно так:

  1. Проверял формат и разрешение файлов с помощью file и sips, уменьшал большой скриншот для общего просмотра.

  2. Вырезал навигацию, заголовок, кнопки и другие сложные области в отдельные изображения и открывал их через инструмент чтения изображений.

  3. С помощью питоновской билиотеки для работы с изображениями — Pillow — считывал цвета пикселей, строил маски относительно фона и находил границы элементов.

  4. Отдельно исследовал приложенные SVG, PNG и WebP: их размеры, альфа‑канал и положение видимой части внутри файла.

  5. Рендерил несколько вариантов шрифтов через `PIL.ImageFont: сравнивал ширину символов и высоту заглавных букв.

Вот реальный фрагмент из сессии при анализе сайта Hermes. Сначала агент разделил скриншот размером 4070×2056 пикселей на зоны и взял несколько проб цвета:

from PIL import Image

im = Image.open("hero.png").convert("RGB")
print(im.size)

im.crop((0, 0, 4070, 460)).save("crop_nav.png")
im.crop((350, 520, 2000, 1900)).save("crop_left.png")
im.crop((400, 1550, 1800, 1850)).save("crop_term.png")

for point in [
    (20, 20), (2000, 1000), (500, 760),
    (700, 1450), (500, 1660), (1000, 1760),
]:
    print(point, im.getpixel(point))

Агент проверил цвет пикселей в нескольких заранее выбранных точках изображения. Сначала он взял две точки на свободных участках фона и получил одинаковое значение (0, 46, 228). Так он определил основной синий цвет страницы #002ee4. Затем проверил точки внутри текста и кнопки: их цвет оказался равен (243, 243, 243), или #f3f3f3. Эти значения агент использовал при создании фона, текста и кнопки в макете. Отдельные увеличенные фрагменты скриншота помогли ему точнее рассмотреть шрифты, границы элементов и внутренние отступы.

Для измерения геометрии Claude Code построил маску: пиксель считался частью элемента, если достаточно сильно отличался от известного фона. Ниже — сокращённый фрагмент скрипта, который использовал агент:

from PIL import Image

im = Image.open("hero.png").convert("RGB")
px = im.load()
background = (0, 46, 228)

def is_foreground(color):
    return sum(abs(color[i] - background[i]) for i in range(3)) > 60

def bbox(x0, y0, x1, y1, label, step=1):
    min_x, min_y = 10**9, 10**9
    max_x, max_y = -1, -1

    for y in range(y0, y1, step):
        for x in range(x0, x1, step):
            if is_foreground(px[x, y]):
                min_x, max_x = min(min_x, x), max(max_x, x)
                min_y, max_y = min(min_y, y), max(max_y, y)

    print(label, min_x, min_y, max_x, max_y)

bbox(300, 650, 2100, 1300, "headline")
bbox(300, 1390, 2100, 1520, "button")
bbox(300, 1600, 2100, 1860, "terminal")

Скрипт измерил видимые границы заголовка, кнопки и других элементов. По этим координатам агент определил их ширину, выравнивание и расстояния между элементами. Размеры контейнеров и внутренние отступы он уточнил отдельно по увеличенным фрагментам изображения.

Вместе со скриншотом сайта Hermes агент получил исходный файл иллюстрации в секции Hero. Чтобы правильно разместить её в макете, он перебирал разные размеры и позиции, накладывая результат на исходный скриншот. Аналогичным способом агент подбирал положение фоновых изображений для PostHog.

Получилось, что анализ скриншота в таблице расходов — это десятки небольших итераций:

обрезка → просмотр → измерение → новая гипотеза → ещё одина обрезка.

Особенно много шагов понадобилось для шрифтов и декоративных изображений, где одного цвета и прямоугольной границы агенту было недостаточно.

Если на входе ссылка

С Tailwind агент действовал иначе. Сначала он получил HTML страницы, нашёл подключённые CSS‑файлы и ассеты, а затем открыл сайт в headless Chrome при фиксированном размере окна 1440×900. Для управления браузером использовался Chrome DevTools Protocol. Внутри страницы агент обошёл DOM и для каждого видимого узла вызвал две функции: getBoundingClientRect() и getComputedStyle().

Ниже сокращён фрагмент JavaScript из сессии — я убрал только обвязку запуска Chrome и подключения к WebSocket:

(() => {
  const out = [];

  const walk = (element, depth) => {
    const rect = element.getBoundingClientRect();
    if (rect.bottom < -60 || rect.top > 1450) return;

    const style = getComputedStyle(element);
    const text = [...element.childNodes]
      .filter(node => node.nodeType === 3)
      .map(node => node.textContent)
      .join("")
      .trim();

    out.push({
      depth,
      tag: element.tagName,
      x: +rect.x.toFixed(1),
      y: +rect.y.toFixed(1),
      width: +rect.width.toFixed(1),
      height: +rect.height.toFixed(1),
      font: `${style.fontFamily} ${style.fontSize}/${style.lineHeight}`,
      weight: style.fontWeight,
      letterSpacing: style.letterSpacing,
      color: style.color,
      background: style.backgroundColor,
      borderRadius: style.borderRadius,
      text: text.slice(0, 90),
    });

    for (const child of element.children) {
      walk(child, depth + 1);
    }
  };

  walk(document.body, 0);
  return JSON.stringify(out);
})();

За один обход страницы агент собрал примерно 430 элементов: их размеры, положение, шрифты, цвета и скругления. Например, он определил, что заголовок набран шрифтом Inter размером 96×96 пикселей. Для панели с кодом и карточки агент также получил точные размеры, отступы, цвета и тени. Все эти данные стали спецификацией для дальнейшей сборки макета.

Во время работы Claude Code решил все равно сделать скриншот страницы. Но использовал он его всего 1 раз, как визуальную проверку. Основные параметры агент получал не по пикселям, а напрямую из страницы.

Отсюда и разница в стоимости анализа. По скриншоту агент вынужден многократно проверять гипотезы, а ссылка позволяет одним обходом DOM снять большую часть геометрии и стилей.

Привожу ниже таблицу‑сравнение стоимости этапа анализа — скриншота сайта в промпте и ссылки.

Входные данные

Сайт

Средняя стоимость анализа

Скриншот

Hermes

$8,20

Скриншот

PostHog

$7,86

Ссылка

Tailwind

$3,56

Куда уходят токены при сборке дизайна

После анализа Claude Code изучает инструкцию MCP, создает элементы, снимает скриншот результата и исправляет отличия.

Цикл выглядит примерно так:

сборка → скриншот → сравнение → исправление → новый скриншот

Создание элементов

В Figma результат создаётся через JavaScript и Plugin API. Сначала агент должен загрузить шрифт, затем создать текстовый слой, задать ему содержимое, размер, высоту строки, межбуквенный интервал, цвет и координаты:

await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });

const h1 = figma.createText();
h1.name = 'Headline';
h1.fontName = { family: 'Inter', style: 'Regular' };
h1.characters = 'Rapidly build modern websites\nwithout ever leaving your HTML.';
h1.fontSize = 96;
h1.lineHeight = { unit: 'PIXELS', value: 96 };
h1.letterSpacing = { unit: 'PIXELS', value: -4.8 };
h1.fills = [{ type: 'SOLID', color: { r: 0.012, g: 0.027, b: 0.071 } }];
h1.resize(1344, 192);
h1.x = 48;
h1.y = 153;
root.appendChild(h1);

Для составных элементов код становился длиннее. Например, кнопку агент создавал как Auto Layout‑фрейм, а текст — как отдельный дочерний слой:

const button = figma.createAutoLayout('HORIZONTAL', { name: 'Get started' });
button.paddingLeft = 16;
button.paddingRight = 16;
button.paddingTop = 8;
button.paddingBottom = 8;
button.cornerRadius = 32;
button.fills = [S('#000000')];

const label = figma.createText();
label.fontName = { family: 'Inter', style: 'Semi Bold' };
label.characters = 'Get started';
label.fontSize = 14;
label.lineHeight = { unit: 'PIXELS', value: 24 };
label.fills = [S('#ffffff')];

button.appendChild(label);
button.x = 48;
button.y = 481;
root.appendChild(button);

Figma MCP создаёт редактируемые нативные слои. Для каждого из них агенту приходится явно описывать свойства.

Особенно много действий потребовалось для воссоздания панели с кодом.

Панель с кодом выделена красным
Панель с кодом выделена красным

В Figma агент создал отдельный текстовый слой для каждой из 14 строк, рассчитал координату y и отдельными вызовами раскрасил диапазоны символов:

lines.forEach((segments, index) => {
  const line = figma.createText();
  line.fontName = { family: 'IBM Plex Mono', style: 'Regular' };
  line.characters = segments.map(segment => segment[0]).join('');
  line.fontSize = 13;
  line.lineHeight = { unit: 'PIXELS', value: 24 };
  line.x = 48;
  line.y = 12 + 24 * index;
  codePanel.appendChild(line);

  let start = 0;
  for (const [text, color] of segments) {
    line.setRangeFills(start, start + text.length, [S(color)]);
    start += text.length;
  }
});

Во время работы с канвасом агент сгенерировал 221,7 тысячи выходных токенов. Нативная структура требует пошагово создавать объекты и явно передавать их свойства. Поскольку выходные токены стоят дороже входных, длинные аргументы инструментов заметно повлияли на итоговую стоимость.

Проверка скриншотов

При проверке главного экрана Tailwind агент увидел, что описание под заголовком «Rapidly build modern websites…» переносится иначе, чем на исходном сайте. Он зафиксировал перенос в нужном месте, изменил ширину текстового блока и повторно запросил его скриншот.

После создания очередного блока агент делал его скриншот и сравнивал с оригиналом. MCP‑функция get_screenshot возвращала не само изображение, а временную ссылку на PNG‑файл:

{
  "image_url": "https://www.figma.com/api/mcp/asset/...png",
  "width": 1440,
  "height": 900
}

Затем агент скачивал файл и отдельно открывал его:

curl -L -o screenshot.png "https://www.figma.com/api/mcp/asset/...png"

То есть цикл выглядел примерно так:

запросить скриншот → получить ссылку → скачать → открыть → исправить элемент

Во время сборки Tailwind агент также написал небольшой JavaScript‑скрипт для сравнения результата с оригиналом. Оба изображения загружались в Canvas, после чего скрипт проходил по массивам пикселей и считал заметные расхождения цветов:

let different = 0;

for (let i = 0; i < reference.length; i += 4) {
  const difference =
    Math.abs(reference[i] - result[i]) +
    Math.abs(reference[i + 1] - result[i + 1]) +
    Math.abs(reference[i + 2] - result[i + 2]);

  if (difference > 60) {
    different++;
  }
}

При каждой проверке появлялись дополнительные команды для скачивания и открытия файла. Поэтому часть расходов на «работу вокруг MCP» возникла уже после того, как агент начал собирать макет.

Итог

Основная часть расходов связана не с самим созданием макета, а с тем, как агент получает данные, управляет канвасом и проверяет результат. Ссылка на сайт оказалась выгоднее скриншота: готовые HTML и CSS позволяют извлечь структуру, стили и ассеты напрямую, не восстанавливая их по пикселям.

Понятно, что макет — это только половина пути. Во второй части я проведу обратный эксперимент: передам Claude Code созданные макеты и попрошу превратить их в работающий код.