Коротка відповідь. 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 очима хоста
- Модель вирішує викликати tool, у якого в
_metaєui/resourceUri. - Хост (до чи паралельно з виконанням) забирає UI-ресурс і готує sandboxed iframe.
- Tool виконується, його результат передається в UI.
- Користувач взаємодіє з інтерфейсом; дії повертаються як JSON-RPC-повідомлення – кожне можна логувати й аудіювати.
- Результат взаємодії (вибір, підтвердження, введені дані) стає частиною розмови – модель бачить структурований підсумок, а не скриншот.
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 – це код, який треба підтримувати й секьюрити |
Типові помилки
- UI без текстового fallback. Спека прямо вимагає graceful degradation – хости без розширення мають отримати повноцінну текстову відповідь.
- Бізнес-логіка всередині iframe. UI – тонкий шар подання; рішення і валідація живуть на сервері, інакше ти довіряєш пісочниці більше, ніж вона обіцяє.
- Спроба обійти consent. «Тихі» tool-виклики з UI – саме те, від чого модель безпеки захищає; дизайнити продукт навколо їх обходу – дорога до делістингу з хостів.
- Ігнорування версії спеки. Це молоде розширення: перевіряй актуальну версію (зараз – 2026-01-26) і підтримку конкретних хостів перед стартом, а точні імена API – по SDK, а не по статтях (включно з цією).
Практичне завдання
Візьми свій (або уявний) MCP-сервер і знайди в ньому один tool, чия відповідь – найболючіша для читання текстом. Опиши на папері: (1) який мінімальний UI вирішив би проблему, (2) які саме дані ходитимуть через JSON-RPC в обидва боки, (3) що буде в текстовому fallback. Якщо на кроці 3 виявиться, що fallback читається нормально, – вітаю, ти щойно заощадив собі ітерацію підтримки UI.