Resources, Tools, Prompts — to trzy prymitywy MCP, które opisała ta seria.
Jest czwarty. Czas nadrobić.
SEP-1865 wszedł do specyfikacji MCP 28 stycznia 2026. Nazywa się MCP Apps i definiuje, jak serwer MCP zwraca interaktywny interfejs użytkownika zamiast surowego tekstu. Przez kilka miesięcy opisywaliśmy wszystko, co dzieje się między klientem a serwerem — JSON-RPC, transport, prymitywy, OAuth, tutorial PHP. Ten wpis domyka obraz.
Czym jest MCP Apps
Kto z kim: serwer MCP → host (klient MCP), przez nowy typ zasobu.
Problem który rozwiązuje: narzędzia MCP zwracają tekst. Tekst jest czytelny dla modelu, ale ubogi dla człowieka. Wykres, formularz, dashboard, interaktywna wizualizacja — tego tekstem nie zrobisz. MCP Apps wprowadza standardowy sposób, w którym serwer może zwrócić HTML w sandboxowanym iframe, a host go wyrenderuje bezpośrednio w interfejsie.
Jak: serwer deklaruje zasoby UI pod schematem ui://, narzędzia linkują te zasoby przez metadane w polu _meta.ui.resourceUri, host renderuje je w izolowanym iframe. Komunikacja między iframe a hostem idzie przez JSON-RPC — ten sam protokół, który poznałeś w artykule o JSON-RPC pod spodem.
Format treści: text/html;profile=mcp-app — HTML który host rozpoznaje jako zasób MCP Apps i renderuje inaczej niż zwykłą stronę.
Transport: bez zmian — Streamable HTTP albo stdio, tak jak reszta MCP. MCP Apps to rozszerzenie, nie nowy protokół.
Kto stworzył: inicjatywa community (Ido Salomon i 8 współautorów), zainspirowana projektem MCP-UI i Apps SDK od OpenAI. Merged do głównej spec przez Anthropic 28.01.2026. Pod Linux Foundation jak cały MCP.
Stan adopcji: wczesny, ale konkretny. Early adopters przed standaryzacją: Postman, HuggingFace, Shopify, Goose, ElevenLabs. Po merge: otwarte implementacje w oficjalnych SDK — TypeScript (gotowy), Ruby (gotowy), Python, Go, Rust, Java, Kotlin, Swift, PHP — każdy ma otwarte issue pod SEP-1865. VS Code ma issue z 5 czerwca 2026. Apollo Client, LibreChat — w trakcie integracji.
Gdzie nie działa: MCP Apps wymaga hosta, który explicite obsługuje wyrenderowanie iframe. Stary klient MCP bez tej obsługi zignoruje _meta.ui.resourceUri i zwróci tylko tekstowy wynik narzędzia — wsteczna kompatybilność jest zachowana.
Jak to działa od środka
Trzy elementy składają się na jedno wywołanie z UI.
1. Zasób UI na serwerze
Serwer deklaruje zasób HTML pod schematem ui://:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerAppTool, registerAppResource } from '@modelcontextprotocol/ext-apps/server';
import { createUIResource } from '@mcp-ui/server';
import { z } from 'zod';
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
// Zasób UI — HTML pod schematem ui://
const widgetUI = createUIResource({
uri: 'ui://my-server/widget',
content: {
type: 'rawHtml',
htmlString: '<h1>Wyniki kampanii</h1><p>CTR: <strong>3.2%</strong></p>'
},
encoding: 'text',
});
// Rejestracja zasobu
registerAppResource(server, 'widget_ui', widgetUI.resource.uri, {}, async () => ({
contents: [widgetUI.resource]
}));
2. Narzędzie które linkuje zasób przez _meta
// Narzędzie linkuje zasób UI przez _meta.ui.resourceUri
registerAppTool(server, 'get_campaign_results', {
description: 'Pobierz wyniki kampanii jako interaktywny widget',
inputSchema: { campaign_id: z.string() },
_meta: { ui: { resourceUri: widgetUI.resource.uri } } // ← tu jest połączenie
}, async ({ campaign_id }) => {
// Wynik tekstowy dla modelu (zawsze obecny)
return {
content: [{ type: 'text', text: `Kampania ${campaign_id}: CTR 3.2%, konwersje 142` }]
};
});
3. Klient renderuje przez AppRenderer
import { AppRenderer } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
return (
<AppRenderer
client={client}
toolName={toolName}
sandbox={{ url: sandboxUrl }}
toolInput={toolInput}
toolResult={toolResult}
onOpenLink={async ({ url }) => {
if (url.startsWith('https://') || url.startsWith('http://')) {
window.open(url);
}
}}
/>
);
}
Wynik: model dostaje tekst (jak zawsze), człowiek widzi HTML w bezpiecznym iframe.
Dlaczego _meta, nie nowy typ treści
To jest nieoczywisty wybór architektoniczny, który warto rozumieć. MCP Apps nie dodaje czwartego elementu do content[]w wynikach narzędzia. Zamiast tego używa _meta — pola zarezerwowanego w JSON-RPC dla niestandardowych metadanych, które model ignoruje.
Powód: model nie renderuje UI. To aplikacja-host go renderuje. Mieszanie treści dla modelu z treścią dla interfejsu w jednej tablicy content[] byłoby złym pomysłem — host musiałby odgadywać, co jest dla kogo. _meta.ui.resourceUri jest jednoznaczne: to wskazówka dla hosta, nie dla modelu.
Stary klient który nie zna MCP Apps ignoruje _meta i dostaje czysty wynik tekstowy. Nowy klient który zna MCP Apps sięga po zasób UI i renderuje iframe. Jeden serwer, dwa typy klientów, bez breaking change.
Model bezpieczeństwa
Sandbox jest tu kluczowy i nieprzypadkowy. Każdy zasób UI działa w izolowanym iframe z ograniczonymi uprawnieniami. Komunikacja między iframe a hostem idzie wyłącznie przez JSON-RPC — loggowalne, audytowalne, bez bezpośredniego dostępu do DOM rodzica. Serwer nie może wyciągnąć danych z hosta bez jawnego wywołania narzędzia, które przejdzie przez normalny mechanizm autoryzacji MCP.
To przedłużenie modelu bezpieczeństwa, który opisaliśmy w artykule o OAuth i autoryzacji: host kontroluje co agent może zrobić, tu host kontroluje też co iframe może zrobić.
Co to znaczy dla builderów PHP i WordPress
PHP SDK ma już otwarte cztery issues pod implementację SEP-1865 (335, 350, 351, 352 w modelcontextprotocol/php-sdk). To oznacza, że serwer MCP napisany w PHP — jak ten z tutorialu na webflux — będzie mógł zwracać zasoby UI przez te same mechanizmy co TypeScript, gdy PHP SDK wdroży spec.
Dziś: możesz już zwracać _meta.ui.resourceUri w odpowiedzi narzędzia ręcznie — to zwykłe pole JSON. Host który obsługuje MCP Apps odbierze je poprawnie. Natywna abstrakcja w SDK przyjdzie z implementacją.
Gdzie to żyje — dwa repozytoria
modelcontextprotocol/ext-apps — oficjalna spec i SDK rozszerzeń. Tu jest definicja registerAppTool, registerAppResource i pełna spec MCP Apps w specification/draft/apps.mdx.
github.com/idosal/mcp-ui — community playground, pakiety @mcp-ui/client i @mcp-ui/server (npm), Ruby gem, PyPI. Tutaj powstała większość wzorców zanim weszły do spec; tu też trafiają eksperymenty z nowymi typami treści poza HTML.
Relacja między nimi jest taka jak między spec a implementacją referencyjną — ext-apps to standard, mcp-ui to plac zabaw i punkt wejścia dla builderów.
Co z tego wynika
MCP Apps to czwarty prymityw — obok Resources, Tools i Prompts — który serwer może wystawić hostowi. Różni się od trzech poprzednich jedną rzeczą: nie jest dla modelu. Jest dla człowieka, który patrzy w ekran. Model dostaje tekst, człowiek dostaje iframe.
Specyfikacja jest w main od 28 stycznia. SDK-i wdrażają. Jeśli budujesz serwer MCP i chcesz, żeby jego wyniki wyglądały jak narzędzie a nie jak ściana tekstu — tu zaczyna się ta droga.
Pojęcia ze słownika: MCP · Prymitywy MCP · JSON-RPC · Streamable HTTP · OAuth dla agentów
Pakiety: @mcp-ui/client · @mcp-ui/server · mcp-ui-server (PyPI) · mcp_ui_server (Ruby)











