Как я добавил в браузерного AI-агента поддержку MCP за вечер

от автора

Что такое n0x и зачем ему MCP

n0x — это open-source AI-воркстейшн, который работает полностью в браузере. LLM, RAG, Python-песочница, генерация изображений. Всё это крутится на WebGPU и WASM. Нет сервера, нет API-ключей, ваши данные не покидают ваш компьютер.

Проект уже умеет многое, но есть нюанс: агент мог только болтать. Он вызывает свои внутренние тулы (поиск, Python, память), но не может взаимодействовать с внешним миром.

А мне хотелось, чтобы на фразу «открой Хабр» он мог реально открыть вкладку с habr.com.

Так я решил добавить MCP.


Что такое MCP (Model Context Protocol)

MCP — это протокол, который позволяет LLM-приложениям вызывать внешние инструменты через стандартизированный интерфейс. Придуман он был в Anthropic (создатели Claude), но стал стандартом по всей экосистеме.

Выглядит это так:

[Пользователь] → [LLM] → [MCP Client] → [MCP Server] → [Инструмент]                     ↓              Результат выполнения

MCP Server — это отдельный процесс, который предоставляет набор тулов. Например, chrome-devtools-mcp даёт возможность управлять браузером: открывать URL, делать скриншоты, выполнять JS.

LLM в ответе пишет что-то на подобии:

{"tool": "chrome_devtools_navigate", "args": {"url": "https://habr.com"}}

MCP клиент перехватывает объект, вызывает сервер, получает результат и отдаёт обратно LLM для генерации финального ответа.


как я встроил MCP в n0x

В n0x используется React + Zustand + Web Workers. MCP-клиент работает в отдельном воркере, чтобы не блокировать UI.

Вот схема:

┌─────────────────────────────────────────────────────────────┐│                         UI (React)                          ││  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐ ││  │ ChatInput   │  │ AgentTrace  │  │ McpToolDrawer       │ ││  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘ │└─────────┼────────────────┼────────────────────┼────────────┘          │                │                    │          ▼                ▼                    ▼┌─────────────────────────────────────────────────────────────┐│                      Zustand Store                          ││  ┌────────────────────────────────────────────────────────┐ ││  │                    useAgent.ts                         │ ││  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────┐  │ ││  │  │ parseToolCall│  │ executeTool  │  │ buildPrompt │  │ ││  │  └──────┬───────┘  └──────┬───────┘  └──────┬──────┘  │ ││  └─────────┼──────────────────┼──────────────────┼────────┘ ││            │                  │                  │          ││            ▼                  ▼                  ▼          ││  ┌────────────────────────────────────────────────────────┐ ││  │                    useMCP.ts                           │ ││  │         (Zustand store + Worker communication)        │ ││  └─────────────────────────┬──────────────────────────────┘ │└────────────────────────────┼────────────────────────────────┘                             │                             ▼┌─────────────────────────────────────────────────────────────┐│                      Web Worker                             ││  ┌────────────────────────────────────────────────────────┐ ││  │                   mcp.worker.ts                        │ ││  │  • Client (StreamableHTTPClientTransport)             │ ││  │  • Tool registration & state management               │ ││  └─────────────────────────┬──────────────────────────────┘ │└────────────────────────────┼────────────────────────────────┘                             │                             ▼┌─────────────────────────────────────────────────────────────┐│                      MCP Server                             ││  ┌────────────────────────────────────────────────────────┐ ││  │   chrome-devtools-mcp + supergateway                  │ ││  │   (отдельный процесс на localhost:8080/mcp)          │ ││  └────────────────────────────────────────────────────────┘ │

Каждый уровень отвечает за своё:

  • UI — отображает панель управления тулами (McpToolDrawer)

  • useAgent.ts — парсит ответы LLM и вызывает тулы

  • useMCP.ts — Zustand-хранилище и коммуникация с воркером

  • mcp.worker.ts — реальные HTTP-запросы к MCP-серверу

  • MCP Server — внешний процесс (chrome-devtools-mcp)


Ключевые компоненты кода

1. Подключение к MCP-серверу (mcp.worker.ts)

async function connectToServer(url: string) {  const serverUrl = new URL(url);  const transport = new StreamableHTTPClientTransport(serverUrl);  const client = new Client({    name: "n0x-mcp-client",    version: "1.0.0"  });    await client.connect(transport);  const { tools } = await client.listTools();    return { client, tools };}

Ничего сложного — создаём транспорт, подключаем клиент, получаем список тулов.

2. Парсинг навигационных команд (useAgent.ts)

Самое интересное для меня было — научить LLM понимать естественный язык:

function parseToolCall(text: string) {  // Паттерны для распознавания команд навигации  const navPatterns = [    /(?:открой|open|goto|перейди|загрузи)\s+(?:https?:\/\/)?([a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(?::\d+)?(?:\/[\w\-./?%&=]*)?)/i,    /^(?:открой|open)\s+([\w\-]+(?:\.[\w\-]+)+)/i,  ];    for (const pattern of navPatterns) {    const match = text.match(pattern);    if (match) {      let url = match[1];      if (!url.startsWith('http')) url = 'https://' + url;      return {        thought: "",        tool: "mcp_navigate",  // имя тула из MCP        args: { url }      };    }  }  // ... остальной парсинг JSON}

Благодаря этой конструкции, фраза «открой яндекс» агент возвращает структурированный вызов тула, а не просто текст.

3. Алиасы для удобства (useChat.ts)

Когда MCP-тулы загружаются, я на всякий случай добавил короткие алиасы:

for (const tool of server.tools) {  if (tool.isEnabled) {    toolkit[tool.name] = async (args) => {      return await mcpState.callTool(server.url, tool.name, args);    };        // Алиасы для удобства пользователей    if (tool.name.includes('navigate')) {      toolkit['open'] = toolkit[tool.name];      toolkit['goto'] = toolkit[tool.name];    }  }}

После этого стало можно писать «open google.com» вместо длинного chrome_devtools_navigate.

4. Системный промпт с инструкциями

Чтобы LLM понимала, когда надо вызывать тулы, я добавил в системный промпт секцию:

BROWSER CONTROL TOOLS:• http___localhost_8080_mcp_navigate — OPEN ANY URL IN BROWSER.CRITICAL INSTRUCTIONS:- When user asks to "open", "go to", "visit", "открой", "перейди" — use navigate tool IMMEDIATELY.- Do NOT respond with just a text link.- ALWAYS add https:// if not specified.

Как это выглядит со стороны пользователя

  1. Пользователь пишет: «открой хабр»

  2. Агент распознаёт команду и вызывает MCP-тул navigate

  3. Chrome открывает новую вкладку с habr.com

  4. Агент отвечает: «✓ Открыл https://habr.com»

Всё происходит достаточно быстро. Никаких лишних кликов.


Подключение

Есть два процесса, которые вместе создают мост между n0x и браузером Chrome:

n0x (браузер) → supergateway (прокси) → chrome-devtools-mcp → Chrome (с отладкой)

Для начала надо запустить Chrome с remote debugging

chromium --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-profile

Проверить, что Chrome запущен правильно:

# В отдельном терминалеcurl http://localhost:9222/json/version

Дальше надо запустить supergateway с chrome-devtools-mcp

npx supergateway --stdio "npx chrome-devtools-mcp@latest --browser-url=http://127.0.0.1:9222" \  --port 8080 --outputTransport streamableHttp

И указать путь в MCP Tools http://localhost:8080/mcp

Вот и все.

Что дальше?

Сейчас я добавил только открытие URL. Но MCP открывает огромные возможности:

  • Скриншоты — «сделай скриншот текущей страницы»

  • Выполнение JS — «найди на странице все заголовки h1»

  • Взаимодействие с DOM — «кликни на кнопку “Войти”»

  • Заполнение форм — «заполни поле поиска словом “MCP”»

В планах сделать полноценного AI-ассистента, который реально работает в браузере, а не просто общение.

Итог

За один вечер я добавил в n0x поддержку MCP. Мне понавилось то, на сколько это просто сделать агента, который может управлять браузером. Главное что я понял: MCP — это мост между LLM и реальным миром. И он уже существует прямо в браузере. Если захотите повторить — все исходники открыты в репозитории n0x.

Полезные ссылки:

ссылка на оригинал статьи https://habr.com/ru/articles/1029300/