Коротка відповідь. MCP Apps – перше офіційне розширення MCP (io.modelcontextprotocol/ui, спека 2026-01-26, співрозробка Anthropic і OpenAI): сервер декларує HTML-інтерфейси як ресурси зі схемою ui://, tool посилається на них через метадані, а хост рендерить їх у sandboxed iframe. UI розмовляє з сервером тим самим MCP JSON-RPC поверх postMessage – структуровано й аудійовано, з user consent на tool-виклики. Це відповідь на реальний біль «результат інструмента неможливо показати текстом», але з жорсткими межами: пісочниця, задекларовані шаблони, обов'язковий текстовий fallback. І головне правило залишається: якщо текст справляється – UI не потрібен.

Проблема: стіна JSON як інтерфейс

Класична сцена агентного продукту: tool повертає таблицю з 40 товарів, модель сумлінно переказує її маркованим списком, користувач шукає потрібний рядок очима. Або гірше – approval flow: «надрукуйте approve, якщо погоджуєтесь». Для продуктів, де людина має обрати, порівняти або підтвердити, текстовий канал – це милиця.

До стандартизації кожен вендор ліпив своє: кастомні компоненти хоста, hardcoded-рендеринг для «особливих» інструментів. MCP Apps робить це частиною протоколу: будь-який сервер може віддати інтерфейс, будь-який хост із підтримкою розширення – показати його. Якщо ти ще не в контексті MCP, спершу основи – тут, а архітектура сучасного сервера розібрана в статті про stateless MCP 2026.

Як це влаштовано

Три будівельні блоки:

1. UI-ресурс. Звичайний MCP-ресурс, але зі схемою ui:// і HTML-вмістом:

// Концептуально, за спекою 2026-01-26; точний API звіряй із SDK
server.registerResource({
  uri: 'ui://approvals/deploy-panel',
  name: 'Deploy approval panel',
  mimeType: 'text/html',
  read: async () => renderApprovalPanelHtml(),
});

2. Зв'язка tool → UI через метадані інструмента:

server.registerTool(
  'request_deploy_approval',
  {
    description: 'Запитує у користувача підтвердження деплою',
    inputSchema: z.object({ service: z.string(), version: z.string() }),
    _meta: { 'ui/resourceUri': 'ui://approvals/deploy-panel' },
  },
  async (args) => ({
    // Обов'язковий текстовий fallback для хостів без підтримки UI
    content: [{ type: 'text', text: `Підтвердь деплой ${args.service}@${args.version}` }],
  })
);

Ключова деталь: ресурс задекларований заздалегідь, тому хост може завантажити і проінспектувати шаблон ще до виконання інструмента – це і security review, і префетч.

3. Двосторонній канал. UI в iframe спілкується з хостом через MCP JSON-RPC поверх postMessage. Користувач натиснув «Approve» – UI надсилає структуроване повідомлення (аж до tool-виклику), і цей виклик проходить через ті самі механізми згоди користувача, що й звичайні дії агента. Жодних прихованих fetch-ів у чужі API зсередини інтерфейсу.

Lifecycle очима хоста

  1. Модель вирішує викликати tool, у якого в _meta є ui/resourceUri.
  2. Хост (до чи паралельно з виконанням) забирає UI-ресурс і готує sandboxed iframe.
  3. Tool виконується, його результат передається в UI.
  4. Користувач взаємодіє з інтерфейсом; дії повертаються як JSON-RPC-повідомлення – кожне можна логувати й аудіювати.
  5. Результат взаємодії (вибір, підтвердження, введені дані) стає частиною розмови – модель бачить структурований підсумок, а не скриншот.

Security: що UI може, а що – ні

Це найкраще продумана частина спеки, і її варто розуміти, перш ніж щось будувати:

  • Пісочниця. Увесь UI-код живе в sandboxed iframe з обмеженими правами – без доступу до сторінки хоста, його cookies чи DOM.
  • Задекларованість. Хост бачить шаблон до рендерингу; динамічно зліплений HTML «на льоту» – поза моделлю довіри.
  • Аудійованість. Комунікація – лише структурований JSON-RPC; довільних каналів назовні немає.
  • Consent. Tool-виклик, ініційований з UI, – це все одно tool-виклик: хост показує його користувачу за своїми правилами.

Рамка та сама, що й у розмові про довіру до рішень моделей: структурованість не гарантує правильності, але робить дії видимими і керованими.

Коли MCP App виправданий, а коли це овер

Чесна таблиця рішення:

Ситуація Рішення
Вибір одного з N (товар, слот, документ) ✅ UI: список/картки замість переліку текстом
Підтвердження ризикованої дії з деталями ✅ UI: approval-панель із diff-ом
Складна форма з валідацією ✅ UI: поля замість «надішли JSON у відповіді»
Візуалізація (графік, таблиця) ✅ UI, якщо хост підтримує; fallback – текстовий підсумок
Відповідь на питання, статус, короткий результат ❌ текст. Крапка
«Зробимо гарно, бо можемо» ❌ кожен UI – це код, який треба підтримувати й секьюрити

Типові помилки

  1. UI без текстового fallback. Спека прямо вимагає graceful degradation – хости без розширення мають отримати повноцінну текстову відповідь.
  2. Бізнес-логіка всередині iframe. UI – тонкий шар подання; рішення і валідація живуть на сервері, інакше ти довіряєш пісочниці більше, ніж вона обіцяє.
  3. Спроба обійти consent. «Тихі» tool-виклики з UI – саме те, від чого модель безпеки захищає; дизайнити продукт навколо їх обходу – дорога до делістингу з хостів.
  4. Ігнорування версії спеки. Це молоде розширення: перевіряй актуальну версію (зараз – 2026-01-26) і підтримку конкретних хостів перед стартом, а точні імена API – по SDK, а не по статтях (включно з цією).

Практичне завдання

Візьми свій (або уявний) MCP-сервер і знайди в ньому один tool, чия відповідь – найболючіша для читання текстом. Опиши на папері: (1) який мінімальний UI вирішив би проблему, (2) які саме дані ходитимуть через JSON-RPC в обидва боки, (3) що буде в текстовому fallback. Якщо на кроці 3 виявиться, що fallback читається нормально, – вітаю, ти щойно заощадив собі ітерацію підтримки UI.