diff --git a/README.md b/README.md index 96e5a14..a7b7fd8 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ What's mcp-ui?InstallationQuickstart • + JSON Schema GeneratorCore ConceptsExamplesRoadmap • @@ -60,7 +61,7 @@ interface HtmlResourceBlock { It's rendered in the client with the `` React component. -The HTML method is limited, and the external app method isn't secure enough for untrusted 3rd party sites. We need a better method. Some ideas we should explore: RSC, remotedom, etc. +`HtmlResource` now supports an experimental `secure` render mode that sanitizes the HTML with DOMPurify instead of using an iframe. This avoids the security pitfalls of embedding untrusted sites. Future improvements may leverage React Server Components or Remote DOM for even better isolation. ### UI Action @@ -127,6 +128,27 @@ yarn add @mcp-ui/server @mcp-ui/client ``` 3. **Enjoy** interactive MCP UIs — no extra configuration required. +## 🧩 JSON Schema Generator + +Generate simple React forms from JSON Schema using the `generateUI` API. + +```tsx +import { generateUI } from "@mcp-ui/generator"; + +const schema = { + type: "object", + properties: { + name: { type: "string" }, + age: { type: "number" }, + color: { type: "string", enum: ["red", "green"] }, + }, +}; + +export default function MyForm() { + return generateUI(schema); +} +``` + ## 🌍 Examples @@ -153,6 +175,10 @@ Drop those URLs into any MCP-compatible host to see `mcp-ui` in action. - [ ] Expand UI Action API (beyond tool calls) - [ ] Do more with Resources and Sampling +## 🌙 Документация на русском + +- [План трансформации проекта](docs/src/ru/transformation-plan.md) + ## 🤝 Contributing Contributions, ideas, and bug reports are welcome! See the [contribution guidelines](https://github.com/idosal/mcp-ui/blob/main/.github/CONTRIBUTING.md) to get started. diff --git a/docs/src/.vitepress/config.ts b/docs/src/.vitepress/config.ts index 9566102..1dc08ee 100644 --- a/docs/src/.vitepress/config.ts +++ b/docs/src/.vitepress/config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'vitepress'; export default defineConfig({ - lang: 'en-US', + lang: 'en-US', // Default language title: 'MCP UI', description: 'MCP-UI Client & Server SDK Documentation', base: '/mcp-ui/', // For GitHub Pages deployment @@ -12,6 +12,7 @@ export default defineConfig({ }, themeConfig: { + // Nav will be common for all locales, or can be localized if needed nav: [ { text: 'Home', link: '/' }, { text: 'Guide', link: '/guide/introduction' }, @@ -23,37 +24,77 @@ export default defineConfig({ // ]} ], - sidebar: { - '/guide/': [ - { - text: 'Overview', - items: [ - { text: 'Introduction', link: '/guide/introduction' }, - { text: 'Getting Started', link: '/guide/getting-started' }, - { text: 'Protocol Details', link: '/guide/protocol-details' }, - ], - }, - { - text: 'Server SDK (@mcp-ui/server)', - items: [ - { text: 'Overview', link: '/guide/server/overview' }, - { text: 'Usage & Examples', link: '/guide/server/usage-examples' }, - // { text: 'API', link: '/guide/server/api' } // Placeholder + // Locale specific configurations + locales: { + root: { + label: 'English', + lang: 'en-US', + sidebar: { + '/guide/': [ + { + text: 'Overview', + items: [ + { text: 'Introduction', link: '/guide/introduction' }, + { text: 'Getting Started', link: '/guide/getting-started' }, + { text: 'Protocol Details', link: '/guide/protocol-details' }, + ], + }, + { + text: 'Server SDK (@mcp-ui/server)', + items: [ + { text: 'Overview', link: '/guide/server/overview' }, + { text: 'Usage & Examples', link: '/guide/server/usage-examples' }, + // { text: 'API', link: '/guide/server/api' } // Placeholder + ], + }, + { + text: 'Client SDK (@mcp-ui/client)', + items: [ + { text: 'Overview', link: '/guide/client/overview' }, + { + text: 'HtmlResource Component', + link: '/guide/client/html-resource', + }, + { text: 'Usage & Examples', link: '/guide/client/usage-examples' }, + // { text: 'API', link: '/guide/client/api' } // Placeholder + ], + }, ], }, - { - text: 'Client SDK (@mcp-ui/client)', - items: [ - { text: 'Overview', link: '/guide/client/overview' }, + }, + ru: { + label: 'Русский', + lang: 'ru-RU', + link: '/ru/guide/introduction', // Link for the language switcher + sidebar: { + '/ru/guide/': [ { - text: 'HtmlResource Component', - link: '/guide/client/html-resource', + text: 'Обзор', // Russian: Overview + items: [ + { text: 'Введение', link: '/ru/guide/introduction' }, // Russian: Introduction + { text: 'Начало работы', link: '/ru/guide/getting-started' }, // Russian: Getting Started + // { text: 'Детали протокола', link: '/ru/guide/protocol-details' }, // Placeholder for future translation + ], }, - { text: 'Usage & Examples', link: '/guide/client/usage-examples' }, - // { text: 'API', link: '/guide/client/api' } // Placeholder + // Add translated sections for Server and Client SDKs when available + // { + // text: 'Server SDK (@mcp-ui/server)', + // items: [ + // { text: 'Обзор', link: '/ru/guide/server/overview' }, + // { text: 'Использование и примеры', link: '/ru/guide/server/usage-examples' }, + // ], + // }, + // { + // text: 'Client SDK (@mcp-ui/client)', + // items: [ + // { text: 'Обзор', link: '/ru/guide/client/overview' }, + // { text: 'Компонент HtmlResource', link: '/ru/guide/client/html-resource' }, + // { text: 'Использование и примеры', link: '/ru/guide/client/usage-examples' }, + // ], + // }, ], }, - ], + } }, socialLinks: [ diff --git a/docs/src/guide/client/html-resource.md b/docs/src/guide/client/html-resource.md index 3dee89e..ae63668 100644 --- a/docs/src/guide/client/html-resource.md +++ b/docs/src/guide/client/html-resource.md @@ -11,6 +11,7 @@ export interface HtmlResourceProps { resource: Partial; onUiAction?: (result: UiActionResult) => Promise; style?: React.CSSProperties; + renderMode?: 'iframe' | 'secure'; } ``` @@ -25,6 +26,9 @@ export interface HtmlResourceProps { ``` If you don't provide a callback for a specific type, the default handler will be used. - **`style`** (optional): Custom styles for the iframe. +- **`renderMode`** (optional): `'iframe'` (default) or `'secure'`. Secure mode + sanitizes the HTML and renders it directly without an iframe. Actions are + triggered by elements with `data-tool` and optional `data-params` attributes. ## How It Works @@ -53,54 +57,15 @@ By default, the iframe stretches to 100% width and is at least 200px tall. You c See [Client SDK Usage & Examples](./usage-examples.md). -## Recommended Usage Pattern +## Secure Renderer (Experimental) -Client-side hosts should check for the `ui://` URI scheme first to identify MCP-UI resources, rather than checking mimeType: - -```tsx -function App({ mcpResource }) { - if ( - mcpResource.type === 'resource' && - mcpResource.resource.uri?.startsWith('ui://') - ) { - return ( - { - console.log('Action:', tool, params); - return { status: 'ok' }; - }} - /> - ); - } - return

Unsupported resource

; -} -``` - -This pattern allows the `HtmlResource` component to handle mimeType-based rendering internally, making your code more future-proof as new content types (like `application/javascript`) are added. - -## Backwards Compatibility - -The `HtmlResource` component maintains backwards compatibility with the legacy `ui-app://` URI scheme: - -- **Legacy Support**: Resources with `ui-app://` URIs are automatically treated as URL content (equivalent to `mimeType: 'text/uri-list'`) even when they have the historically incorrect `mimeType: 'text/html'` -- **Automatic Detection**: The component detects legacy URIs and processes them correctly without requiring code changes -- **MimeType Override**: Ignores the incorrect `text/html` mimeType and treats content as URLs -- **Migration Encouragement**: A warning is logged when legacy URIs are detected, encouraging server updates -- **Seamless Transition**: Existing clients continue working with older servers during migration periods - -### Legacy URI Handling - -```tsx -// Both patterns work identically: -// Legacy (automatically detected and corrected): - -// Modern (recommended): - -``` +When `renderMode` is set to `"secure"`, the HTML is sanitized using +[`DOMPurify`](https://github.com/cure53/DOMPurify) and injected directly into the +page. No iframe is used. Interactive elements should emit actions by including a +`data-tool` attribute and an optional `data-params` JSON string. ## Security Notes -- **`sandbox` attribute**: Restricts what the iframe can do. `allow-scripts` is needed for interactivity. `allow-same-origin` is external apps. Caution - the external app method isn's not a secure way to render untrusted code. We're working on new methods to alleviate security concerns. -- **`postMessage` origin**: When sending messages from the iframe, always specify the target origin for safety. The component listens globally, so your iframe content should be explicit. -- **Content Sanitization**: HTML is rendered as-is. If you don't fully trust the source, sanitize the HTML before passing it in, or rely on the iframe's sandboxing. +- **`sandbox` attribute**: Restricts what the iframe can do. `allow-scripts` is needed for interactivity. `allow-same-origin` is only used for `ui-app://` URLs. +- **`postMessage` origin**: When sending messages from the iframe, always specify the target origin for safety. +- **Content Sanitization**: In `secure` mode the HTML is sanitized with DOMPurify before rendering. In `iframe` mode the HTML is rendered as-is and relies on the iframe's sandboxing. diff --git a/docs/src/guide/getting-started.md b/docs/src/guide/getting-started.md index 88b6c03..3503889 100644 --- a/docs/src/guide/getting-started.md +++ b/docs/src/guide/getting-started.md @@ -12,7 +12,7 @@ This guide will walk you through setting up your development environment and usi 1. **Clone the Monorepo**: ```bash - git clone https://github.com/idosal/mcp-ui.git # TODO: Update this link + git clone https://github.com/idosal/mcp-ui.git cd mcp-ui ``` diff --git a/docs/src/ru/guide/getting-started.md b/docs/src/ru/guide/getting-started.md new file mode 100644 index 0000000..a3e7bcf --- /dev/null +++ b/docs/src/ru/guide/getting-started.md @@ -0,0 +1,174 @@ +# Начало работы + +Это руководство проведет вас через настройку вашей среды разработки и использование пакетов MCP-UI SDK. + +## Предварительные требования + +- Node.js (рекомендуется v22.x) +- pnpm (рекомендуется v9 или более поздняя) + +## Установка + +1. **Клонируйте Монорепозиторий**: + + ```bash + git clone https://github.com/idosal/mcp-ui.git + cd mcp-ui + ``` + +2. **Установите Зависимости**: + Из корня монорепозитория `mcp-ui` выполните: + ```bash + pnpm install + ``` + Эта команда устанавливает зависимости для всех пакетов (`shared`, `client`, `server`, `docs`) и связывает их вместе с помощью pnpm. + +## Сборка Пакетов + +Для сборки всех библиотечных пакетов (`shared`, `client`, `server`): + +```bash +pnpm --filter=!@mcp-ui/docs build +``` + +Каждый пакет использует Vite для сборки и выводит распространяемые файлы в соответствующий каталог `dist`. + +## Запуск Тестов + +Для запуска всех тестов в монорепозитории с использованием Vitest: + +```bash +pnpm test +``` + +Или для покрытия: + +```bash +pnpm run coverage +``` + +## Использование Пакетов + +После сборки вы обычно можете импортировать из пакетов так же, как и из любого другого модуля npm, при условии, что ваш проект настроен на их разрешение (например, если вы публикуете их или используете такой инструмент, как `yalc` для локальной разработки вне этого монорепозитория). + +### В проекте Node.js (Пример на стороне сервера) + +```typescript +// main.ts (ваше серверное приложение) +import { createHtmlResource } from '@mcp-ui/server'; + +const myHtmlPayload = `

Привет от Сервера!

Временная метка: ${new Date().toISOString()}

`; + +const resourceBlock = createHtmlResource({ + uri: 'ui://server-generated/item1', + content: { type: 'rawHtml', htmlString: myHtmlPayload }, + delivery: 'text', +}); + + +// Отправьте этот resourceBlock как часть вашего ответа MCP... +``` + +### В проекте React (Пример на стороне клиента) + +```tsx +// App.tsx (ваше приложение React) +import React, { useState, useEffect } from 'react'; +import { HtmlResource } from '@mcp-ui/client'; + +// Фиктивная структура ответа MCP +interface McpToolResponse { + content: HtmlResource[]; +} + +function App() { + const [mcpData, setMcpData] = useState(null); + + // Симуляция получения данных MCP + useEffect(() => { + const fakeMcpResponse: McpToolResponse = { + content: [ + { + type: 'resource', + resource: { + uri: 'ui://client-example/dynamic-section', + mimeType: 'text/html', + text: '

Динамический контент через MCP-UI

', + }, + }, + ], + }; + setMcpData(fakeMcpResponse); + }, []); + + const handleResourceAction = async ( + tool: string, + params: Record, + ) => { + console.log(`Действие от ресурса (инструмент: ${tool}):`, params); + // Добавьте вашу логику обработки (например, инициируйте последующий вызов инструмента) + return { status: 'Действие получено клиентом' }; + }; + + return ( +
+

Клиентское приложение MCP

+ {mcpData?.content.map((item, index) => { + if ( + item.type === 'resource' && + item.resource.mimeType === 'text/html' + ) { + return ( +
+

Ресурс: {item.resource.uri}

+ +
+ ); + } + return

Неподдерживаемый элемент контента

; + })} +
+ ); +} + +export default App; +``` + +Далее изучите конкретные руководства для каждого пакета SDK, чтобы узнать больше об их API и возможностях. + +Чтобы собрать именно этот пакет из корня монорепозитория: + +```bash +pnpm build -w @mcp-ui/server +``` + +Смотрите страницу [Использование Server SDK и Примеры](./server/usage-examples.md) для практических примеров. + +Чтобы собрать именно этот пакет из корня монорепозитория: + +```bash +pnpm build -w @mcp-ui/client +``` + +Смотрите следующие страницы для получения более подробной информации: + +## Базовая настройка + +Для серверов MCP убедитесь, что `@mcp-ui/server` доступен в вашем проекте Node.js. Если вы работаете вне этого монорепозитория, вы обычно устанавливаете их. + + +Для клиентов MCP убедитесь, что `@mcp-ui/client` и его одноранговые зависимости (`react` и потенциально `@modelcontextprotocol/sdk`) установлены в вашем проекте React. + +```bash +pnpm add @mcp-ui/client react @modelcontextprotocol/sdk +``` diff --git a/docs/src/ru/guide/introduction.md b/docs/src/ru/guide/introduction.md new file mode 100644 index 0000000..7602f34 --- /dev/null +++ b/docs/src/ru/guide/introduction.md @@ -0,0 +1,56 @@ +# Введение + +Добро пожаловать в документацию MCP-UI! + +Этот SDK предоставляет инструменты для создания приложений с поддержкой Model Context Protocol (MCP) и интерактивными компонентами пользовательского интерфейса. Он направлен на стандартизацию того, как модели и инструменты могут запрашивать отображение насыщенных HTML-интерфейсов в клиентском приложении. + +## Что такое MCP UI? + +MCP UI — это TypeScript SDK, содержащий: + +- **`@mcp-ui/client`**: Компоненты пользовательского интерфейса (например, ``) для простого рендеринга интерактивных HTML-ресурсов. +- **`@mcp-ui/server`**: Вспомогательные функции (например, `createHtmlResource`) для серверной логики, позволяющие легко создавать объекты `HtmlResource`. + +## Основная концепция: Протокол интерактивных HTML-ресурсов + +Центральным элементом этого SDK является `HtmlResource`. Этот объект определяет контракт того, как интерактивный HTML-контент должен структурироваться и доставляться с сервера/инструмента клиенту. + +### Структура `HtmlResource` + +```typescript +// Определено в @mcp-ui/shared, но показано здесь для ясности +export interface HtmlResource { + type: 'resource'; // Фиксированный идентификатор типа + resource: { + uri: string; // Уникальный идентификатор. Управляет поведением рендеринга. + mimeType: 'text/html'; // Должен быть text/html. + text?: string; // Необработанная строка HTML или строка URL iframe. + blob?: string; // Строка HTML или строка URL iframe в кодировке Base64. + }; +} +``` + +### Подробности ключевых полей: + +- **`uri` (Uniform Resource Identifier)**: + - Если начинается с `ui://` (например, `ui://my-custom-form/instance-01`): + - Клиент должен рендерить HTML-контент (из `text` или `blob`) напрямую, обычно в песочнице `