MCP App Proxyfier

MCP App Proxyfier

MCP server delivering interactive MCP Apps UI for Megamarket shopping, enabling search, product details, cart, and checkout through native interfaces within the chat.

Category
访问服务器

README

MCP App Proxyfier

MCP-сервер, который отдаёт в чат с моделью интерактивные MCP Apps (официальное UI-расширение MCP, рендерится в песочнице-iframe хоста). Вместо текстовой обёртки над запросами зритель получает нативный UI прямо в диалоге.

Реализован один вылизанный флоу на настоящих данных: Megamarket — поиск товаров → грид → детальная страница → корзина → оформление.

Данные статические: каталог собран из сохранённых снапшотов реальных страниц megamarket.ru (pages/) скриптом pnpm update:data. Сеть и браузер на демо не нужны — сервер стартует мгновенно и отвечает одинаково при любом Wi-Fi в зале.

Сценарий живого демо (без MCP → MCP без UI → MCP с UI → MCP с UI и скиллом) — в DEMO.md.

Структура

packages/
  ui/      React + Vite; собирается в самодостаточные HTML (по одному на приложение)
  server/  MCP-сервер, отдаёт UI как ui:// ресурсы + инструменты
pages/     HAR/HTML-снапшоты megamarket.ru — сырьё для pnpm update:data

UI собирается в два самодостаточных HTML-бандла — index (каркасный ping) и megamarket; все JS/CSS встроены, внешних ссылок нет (требование песочницы-iframe). Сервер на старте читает эти HTML и регистрирует как ui:// ресурсы.

Инструменты

Инструмент Вход UI Назначение
ping echo? ping.html Каркасная проверка рендера iframe
search_products query, filters? — Поиск товаров, результат только текстом (список позиций)
search_products_widget query, filters? megamarket.html Тот же поиск + грид карточек виджетом
search_products_advised query, filters? megamarket.html Тот же поиск + виджет; описание обязывает прочитать skill://shopping-advisor
get_product id megamarket.html Карточка товара: галерея, таблица «О товаре», описание
get_delivery_calendar — — Сегодня/завтра + ближайшие 7 дней с днями недели
add_to_cart id megamarket.html Добавляет товар в корзину
view_cart — megamarket.html Текущее состояние корзины
checkout — megamarket.html Оформляет заказ по корзине, возвращает подтверждение и очищает её

filters — ценовой коридор priceMin / priceMax и срок доставки deliveryBy (YYYY-MM-DD; оставляет только то, что приедет не позже).

get_delivery_calendar существует потому, что у модели нет часов: «до пятницы» она сама в число не превратит — либо выдумает, либо отсчитает от даты обучения. Скилл обязывает вызвать календарь до поиска, отсюда и порядок вызовов в демо.

Три варианта поиска — это ступени живого демо (без UI → с UI → с UI и методичкой). Они существуют одновременно на одном подключении, переключение идёт формулировкой запроса, без перезапуска сервера. Почему их три, а не один с параметром: привязка UI живёт в _meta.ui.resourceUri на регистрации инструмента и уезжает клиенту в tools/list — результат вызова её изменить не может.

Корзина — in-memory, одна на процесс сервера: перезапуск её обнуляет.

Ресурсы ui://

MCP Apps: самодостаточный HTML, который хост рендерит в песочнице-iframe и кормит structuredContent результата инструмента через мост. MIME — text/html;profile=mcp-app.

URI Собирается из Кто рендерит
ui://mcp-app-proxyfier/ping.html packages/ui/index.html → dist/index.html ping
ui://mcp-app-proxyfier/megamarket.html packages/ui/megamarket.html → dist/megamarket.html все инструменты Megamarket

Приложение Megamarket — мини-SPA: выдача → деталка → корзина → подтверждение. Какой вид показать, оно решает по форме пришедшего structuredContent: products — выдача, product — деталка, cart — корзина.

Виджет интерактивный, а не картинка:

  • чипы фильтров над гридом (бренд, шумоподавление) — фильтруют внутри iframe, без вызова инструмента и без нового пузыря в чате;
  • клик по карточке открывает деталку (get_product через мост), «Назад» возвращает в тот же отфильтрованный список — состояние фильтров переживает переход;
  • деталка открывается и голосом («покажи подробнее вот эти») — вид тот же самый;
  • «В корзину» на карточке и на деталке — app-initiated add_to_cart(id); ответ несёт актуальную корзину, поэтому бейдж обновляется без отдельного view_cart.

Фильтры виджета сознательно не трогают доставку: срок задаёт агент через filters.deliveryBy на сервере. Иначе виджет молча показывал бы то, что агент уже отсёк.

Ресурсы skill://

Методички для агента. В отличие от ui:// это не MCP Apps — рендерить нечего, это обычный текст, который агент читает перед вызовом инструмента.

URI MIME Назначение
skill://index.json application/json Индекс скиллов — точка входа, по которой агент находит остальные
skill://shopping-advisor/SKILL.md text/markdown Подбор товара: уточнить бюджет и сценарий, перевести бюджет в filters, сравнивать по цене и объёму отзывов, не вестись на витринную скидку

Индекс и сами методички собираются из одного SkillDefinition (packages/server/src/skills/skill-registry.ts), поэтому имя и описание в индексе не могут разъехаться с ресурсом.

Данные

Каталог — packages/server/data/market.json (70 товаров, у всех есть детальная карточка). Пересобирается из снапшотов:

pnpm update:data              # разбирает pages/ → packages/server/data/market.json
pnpm update:data -- --dry-run # только показать, что распарсилось, ничего не писать

Скрипт идемпотентен: повторный прогон просто перезаписывает файл. Сеть не трогает.

Выдача поиска ограничена девятью позициями (SEARCH_RESULT_LIMIT) — грид 3×3 в узком чат-iframe.

Сроки доставки — синтетические

packages/server/data/delivery.json не собирается из снапшотов: настоящий срок (calculatedDeliveryDate в SSR-стейте страниц) есть ровно у одного товара каталога из 70, а на выдаче Мегамаркет его не отдаёт вовсе. Формат подписей при этом взят у сайта: он пишет только «Сегодня», «Завтра» и дату вида «15 июля» — «Послезавтра» у него нет.

В файле лежат дни, а не даты: дата считается в рантайме от сегодня, поэтому «Завтра» остаётся завтрашним и через месяц. Товары, которых в файле нет, получают детерминированный срок по хешу id — одинаковый между запусками, чтобы выдача не «дышала».

Флаг anc (активное шумоподавление) поднимается в «плоский» DTO из характеристик, чтобы виджет фильтровал грид без запроса деталки на каждый товар. null означает «характеристики нет в снапшоте», а не «шумоподавления нет».

Требования

  • Node.js 22+ (рекомендуется 24)
  • pnpm 11+

Сборка

pnpm install
pnpm build          # сначала собирает HTML-бандлы UI, затем сервер

pnpm build сначала собирает UI (самодостаточные HTML со встроенными JS/CSS — без внешних источников, как требует песочница-iframe), затем компилирует сервер, который на старте читает эти HTML и регистрирует как ui:// ресурсы.

Превью виджета в браузере

Посмотреть виджет без Claude Desktop:

pnpm --filter @mcp-app-proxyfier/ui exec vite
# → http://localhost:5173/preview.html

Рендерит те же компоненты вью, что и боевое приложение, на настоящем ответе сервера. Работают чипы фильтров, заход в карточку и возврат в отфильтрованный список. Кейс открывается ссылкой: ?case=friday, ?case=detail, ?case=cart.

Фикстуры пересобираются с живого сервера, руками их править не надо:

pnpm build            # превью читает ответы собранного сервера
pnpm update:fixtures  # → packages/ui/src/preview/fixtures.json

Даты доставки в фикстуре заморожены на момент снятия (превью — про вёрстку, не про календарь). Протухли подписи вроде «Завтра» — просто перезапустите update:fixtures.

Чего в превью нет: postMessage-моста и вызовов инструментов — «Подробнее» берёт деталку из фикстуры, а не дёргает get_product; «В корзину» показывает снимок корзины, а не вызывает add_to_cart. Мост и рендер iframe проверяются только вживую, в Claude Desktop (см. «Ручная проверка рендера» ниже). В продакшен-сборку превью не попадает: pnpm build:ui собирает только index и megamarket.

Тесты

pnpm test           # typecheck (включая тесты) + прогон

Тесты проверяют внешнее поведение через швы: инструменты MCP как чёрные ящики (поднимается настоящий McpServer на in-memory транспорте), загрузку статического каталога (включая деградацию товара без богатой детали), сроки доставки, реестр скиллов и выбор транспорта. Рендер iframe проверяется вручную (см. ниже) — в CI его нет.

Тесты тайпчекаются вместе с кодом: гоняет их tsx, который типы не проверяет, поэтому раньше tsc молча зеленел на тестах, ссылающихся на удалённые модули. Сборка идёт отдельным конфигом (tsconfig.build.json), чтобы тесты не попадали в dist/.

Запуск / регистрация в Claude Desktop

Сервер говорит по MCP через stdio: Claude Desktop запускает его как дочерний процесс. После pnpm build пропишите его в конфиг Claude Desktop.

Расположение файла конфигурации:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json

Добавьте (замените путь на абсолютный путь к этому репозиторию):

{
  "mcpServers": {
    "mcp-app-proxyfier": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-app-proxyfier/packages/server/dist/index.js"]
    }
  }
}

Затем полностью закройте и заново откройте Claude Desktop.

Ручная проверка рендера (главный риск демо)

Главный риск — баг рендера iframe на хосте (ext-apps #671): клиент согласует UI-возможность и тянет ресурс, но не рисует iframe. Поэтому его проверяют глазами на боевой сборке.

В чате Claude Desktop попросите модель вызвать инструмент ping (например, «вызови инструмент ping с echo hello»). Убедитесь визуально:

  1. В чате нарисован интерактивный iframe (карточка с заголовком «MCP App Proxyfier»), а не только текстовый результат.
  2. Карточка показывает message: pong, echo: hello и таймстамп — то есть structuredContent инструмента дошёл до UI через мост.

Если виден только текст и iframe не рисуется — баг #671 воспроизведён: зафиксируйте версию Claude Desktop и держите наготове запасной текстовый сценарий для демо.

Абстракция транспорта

Сервер не зависит от транспорта. Инструменты и ресурсы регистрируются на McpServer без знания о канале. Транспорт выбирается за швом ServerTransportProvider (packages/server/src/transport/): stdio (по умолчанию) и http (Streamable HTTP). Оба провайдера регистрируют ровно те же инструменты и ui:// ресурсы — добавление HTTP не потребовало правок кода инструментов или ресурсов.

Транспорт выбирается флагом или переменной окружения (флаг приоритетнее):

Параметр Флаг Env По умолчанию
Транспорт --transport stdio|http MCP_TRANSPORT stdio
Интерфейс прослушивания --host MCP_HTTP_HOST 127.0.0.1
Порт --port MCP_HTTP_PORT 3000
Путь эндпоинта --path MCP_HTTP_PATH /mcp
Bearer-токен --token MCP_HTTP_TOKEN (выкл.)

Флаги понимают обе формы: --port 3000 и --port=3000.

Удалённый коннектор (Streamable HTTP)

Для демо, где владелец подключает коннектор сам (custom connector в claude.ai), а не Claude Desktop запускает его локально. Это альтернативный канал к тому же серверу — stdio-демо он не блокирует.

  1. Соберите и запустите сервер по HTTP (слушает на 127.0.0.1:3000/mcp). Туннель делает порт публичным, поэтому задайте MCP_HTTP_TOKEN — запросы без Authorization: Bearer <token> отклоняются с 401:

    pnpm build
    MCP_TRANSPORT=http MCP_HTTP_TOKEN="$(openssl rand -hex 16)" pnpm start
    # токен выкл. (только локально, без туннеля): pnpm start -- --transport http
    
  2. Откройте публичный HTTPS-туннель к этому локальному порту:

    cloudflared tunnel --url http://127.0.0.1:3000
    #   → печатает https://<random>.trycloudflare.com
    # альтернатива ngrok:
    #   ngrok http 3000   → https://<random>.ngrok-free.app
    

    URL коннектора — это origin туннеля плюс путь эндпоинта, например https://<random>.trycloudflare.com/mcp.

  3. В claude.ai → Settings → Connectors → Add custom connector вставьте этот URL (и Bearer-токен в поле авторизации коннектора, если вы его задали). Claude инициализирует сессию Streamable HTTP и показывает те же инструменты и ui:// приложения, что и stdio.

Ручная проверка (рендер iframe на хосте, ext-apps #671). Как и для stdio, убедитесь визуально, что результат инструмента рисует интерактивный iframe в чате claude.ai, а не только текст. Баг рендера #671 — клиентский и не связан с транспортом, но его нужно перепроверить на claude.ai: сборка хоста отличается от Claude Desktop.

Замечания:

  • Один запущенный процесс держит одну сессию Streamable HTTP — одного докладчика за туннелем.
  • Реконнект = перезапуск. При чистом отключении claude.ai шлёт завершение сессии и повторное подключение работает; после грязного обрыва (туннель умер) проще всего Ctrl-C и заново --transport http, если коннектор потерял сессию.
  • Прослушивание остаётся на localhost намеренно; не слушайте 0.0.0.0 — доступ к серверу только через туннель. Защита от DNS-rebinding выключена намеренно (host туннеля динамический); доступ охраняет Bearer-токен MCP_HTTP_TOKEN.
  • После демо погасите туннель — URL является секретом.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选