Busymate AI

Разработчикам

Дайте своему ИИ-ассистенту настоящие инструменты через MCP

Дайте ассистенту настоящие инструменты через MCP — открытый стандарт подключения ИИ к вашим системам. Запустите его в вебе, на iOS, Android и десктопе. Управляйте всем из любого MCP-клиента.

claude mcp add --transport http busymate-ai https://busymate.ai/mcp
ТЕРМИНАЛ$claude mcp add --transport http busymate-ai https://busymate.ai/mcpподключеноJSON-RPC 2.0 · tools/call{ "jsonrpc": "2.0", "id": 12, "method": "tools/call", "params": { "name": "get_my_account", "arguments": {} } }{ "jsonrpc": "2.0", "id": 12, "result": { "content": [{ "type": "text", "text": "…" }] } }

Точки входа

Выберите, с чего начать

Добавьте ассистента на свой сайт

Один тег скрипта на любой разрешённой странице — лончер и гостевой чат работают сразу.

Встройте ИИ-поддержку в приложение для iOS

Загрузите чат в WKWebView; небольшой мост из трёх сообщений авторизует ваших пользователей.

Встройте ИИ-поддержку в приложение для Android

Загрузите чат в WebView; один JavaScript-интерфейс авторизует ваших пользователей.

Подключите свой MCP-сервер как инструменты

Ваш собственный MCP-сервер становится действиями ассистента — от имени каждого клиента.

Управляйте всеми рабочими пространствами из терминала

Ведите платформу из Claude Code, Cursor или любого MCP-клиента по OAuth.

Дайте ИИ-агентам читать ваш сайт

Текстовая карта вашего сайта по адресу /llms.txt, написанная для ИИ-агентов.

01Быстрый старт

Как всё устроено

Ваше приложение остаётся источником истины. Ассистент получает короткоживущий подписанный токен, который говорит, кто присутствует, и вызывает ваши инструменты как отдельный актор с ограниченными правами.

Ваши клиентыВаш сайт или приложениеАссистентВаши системыВаши настройкиприменяются автоматически

01

Ваше приложение

Хранит личность и данные аккаунта; подписывает двухминутный токен о том, кто авторизован.

02

Ассистент

Применяет настройки вашего рабочего пространства: контент, модели, уровни доступа инструментов, шаги подтверждения, передачу оператору.

03

Ваш MCP-сервер

Отвечает инструментами для одного пользователя за раз, беря пользователя из bearer-токена входа, а не из аргументов инструмента.

Вариантов подключения: 7, ассистент один: Встраивание на сайт · Авторизованные клиенты · iOS · Android · Десктоп · REST API · Ваш MCP-сервер

YOUR HELP SITErobots-aware1–50 pagesIndexRetrieve for an answerwith a source

02Авторизованные клиенты

Узнавайте клиента, который уже авторизован

Чтобы ассистент мог доверять тому, кто спрашивает, одна аутентифицированная точка на вашем бэкенде подписывает короткоживущий токен (JWT на ES256) с вашим постоянным идентификатором клиента и несколькими безопасными полями для отображения.

Токен живёт не дольше 120 секунд, несёт одноразовый nonce и jti и называет ваш тенант. Приватный ключ и сессия вашего продукта никогда не попадают ни в браузер, ни в чат.

Your backendThe browserThe assistantsigns the tokenthe key never leavescarries itchecks your JWKS120s maxchecked
server/bmai-identity.tstypescript
import { SignJWT, importJWK } from "jose";

// POST https://YOUR-PRODUCT-DOMAIN/api/bmai/identity — requires YOUR OWN logged-in product session.
app.post("/api/bmai/identity", requireSession, async (req, res) => {
  // Fresh on EVERY mint. Never persist the launch token/nonce in localStorage,
  // sessionStorage, cookies, React state, or module state: every assistant
  // launch consumes this pair exactly once.
  const nonce = typeof req.body?.nonce === "string" ? req.body.nonce : "";
  if (!/^[A-Za-z0-9_-]{32,200}$/.test(nonce)) return res.status(400).json({ error: "invalid_nonce" });
  const key = await importJWK(JSON.parse(process.env.AI_LAUNCH_PRIVATE_JWK), "ES256");
  const token = await new SignJWT({
      "tenant_id": "00000000-0000-0000-0000-000000000000", // Your product's registered tenant claim
      nonce,                               // equals the sibling field below
      name: req.user.displayName,          // optional low-sensitivity display claim
    })
    .setProtectedHeader({ alg: "ES256", kid: process.env.AI_LAUNCH_KEY_ID })
    .setIssuer("https://YOUR-PRODUCT-DOMAIN (set when you register the provider)")
    .setAudience("busymate-ai")
    .setSubject(req.user.id)               // IMMUTABLE internal account id; never email/phone/session id
    .setJti(crypto.randomUUID())           // one-time (replay-protected)
    .setIssuedAt()
    .setExpirationTime("120s")               // <= registered max age (120s)
    .sign(key);
  res.set("Cache-Control", "no-store");
  res.status(201).json({ token, nonce, expiresIn: 120 });
});

Токен говорит, кто пришёл, а не что ему позволено. Держите балансы, устройства, настройки и любые изменения за инструментами, которые отвечают только для одного пользователя.

03Веб и полная страница

Один скрипт для встроенного лончера

SDK запрашивает личность у вашего бэкенда, когда она нужна, работает для гостей, обновляется после входа и выхода, открывает внешние ссылки вне фрейма и не требует сторонних cookie.

index.htmlhtml
<!-- Floating "bro" launcher for Your product.
     Anonymous chat works immediately on the allowed origins. -->
<script>
  function newLaunchNonce() {
    const bytes = new Uint8Array(32);
    crypto.getRandomValues(bytes);
    return btoa(String.fromCharCode(...bytes))
      .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
  }

  window.BusymateAI = {
    getIdentity: async () => {
      const accessToken = await getProductAccessToken(); // YOUR existing auth helper
      if (!accessToken) return null;
      const nonce = newLaunchNonce();
      const returnTo = new URL(location.href); returnTo.search = ""; returnTo.hash = "";
      const r = await fetch("https://YOUR-PRODUCT-DOMAIN/api/bmai/identity", {
        method: "POST", credentials: "include", cache: "no-store",
        headers: { "content-type": "application/json", authorization: "Bearer " + accessToken },
        body: JSON.stringify({ nonce, returnTo: returnTo.href }),
      });
      if (r.status === 401) return null; // signed out -> anonymous chat
      if (!r.ok) throw new Error("AI identity mint failed (" + r.status + ")");
      // MUST be a newly minted { token, nonce } pair on every call. Never
      // persist either value in localStorage, sessionStorage, cookies, React
      // state, or module state.
      const identity = await r.json();
      if (identity.nonce !== nonce) throw new Error("AI identity nonce mismatch");
      return { token: identity.token, nonce: identity.nonce };
    },
  };

  // Call after YOUR product completes login, logout, access-token/session
  // rotation, or account switch. Do not send an identity postMessage directly.
  window.refreshAssistantIdentity = () =>
    window.BusymateAI?.refreshIdentity?.() ?? Promise.resolve();
</script>
<script
  src="https://your-assistant.busymate.ai/embed/v1.js"
  data-assistant="your-assistant"
  data-label="Ask bro"
  async></script>

Откройте полностраничный чат по вашему адресу

Когда авторизованный пользователь открывает адрес вашего ассистента или ваш собственный домен, передайте токен входа во фрагменте URL. Страница очищает его до обмена.

open-assistant.tstypescript
// Full-page open with identity in the URL FRAGMENT — never sent in the
// request line, referrer, or logs; the destination strips it before exchange.
function newLaunchNonce() {
  const bytes = new Uint8Array(32);
  crypto.getRandomValues(bytes);
  return btoa(String.fromCharCode(...bytes))
    .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
}

const nonce = newLaunchNonce();
const accessToken = await getProductAccessToken(); // YOUR existing auth helper
if (!accessToken) throw new Error("Sign in before opening an identified assistant");
const returnTo = new URL(location.href); returnTo.search = ""; returnTo.hash = "";
const response = await fetch("https://YOUR-PRODUCT-DOMAIN/api/bmai/identity", {
  method: "POST", credentials: "include", cache: "no-store",
  headers: { "content-type": "application/json", authorization: "Bearer " + accessToken },
  body: JSON.stringify({ nonce, returnTo: returnTo.href }),
});
if (response.status === 401) throw new Error("Sign in before opening an identified assistant");
if (!response.ok) throw new Error("AI identity mint failed (" + response.status + ")");
const identity = await response.json();
if (typeof identity.token !== "string" || typeof identity.nonce !== "string") {
  throw new Error("AI identity mint returned an invalid response");
}
if (identity.nonce !== nonce) throw new Error("AI identity nonce mismatch");

const url = new URL("https://your-assistant.busymate.ai/");
url.hash = new URLSearchParams({
  bmai_token: identity.token,
  bmai_nonce: identity.nonce,
}).toString();
location.assign(url);

04iPhone

WKWebView с узким мостом сообщений

Разрешите только три версионированных сообщения: запрос личности, ответ с личностью и внешний URL. Выдавайте личность через уже существующий аутентифицированный API-клиент приложения.

AssistantBridge.swiftswift
// SDK source: https://busymate.ai/sdk/v1/ios/BusymateAI.swift
// Load https://your-assistant.busymate.ai/?channel=ios in a WKWebView.
final class AssistantBridge: NSObject, WKScriptMessageHandler {
  let webView: WKWebView

  func userContentController(_ controller: WKUserContentController,
                             didReceive message: WKScriptMessage) {
    guard message.name == "BusymateAI",
          let body = message.body as? [String: Any],
          body["type"] as? String == "busymate.ai.v1.identity_request"
    else { return }

    Task { // mint through YOUR authenticated API — never a key in the app
      let identity = try await api.mintLaunchIdentity()
      let payload: [String: Any] = [
        "type": "busymate.ai.v1.identity",
        "token": identity.token,
        "nonce": identity.nonce,
      ]
      let data = try JSONSerialization.data(withJSONObject: payload)
      let json = String(decoding: data, as: UTF8.self)
      await webView.evaluateJavaScript("window.postMessage(\(json), '*')")
    }
  }
}

05Android

WebView с одним разрешённым интерфейсом

Оставляйте навигацию в пределах AI-домена тенанта, открывайте внешние ссылки в системном браузере и не предоставляйте универсальные нативные методы.

AssistantBridge.ktkotlin
// SDK source: https://busymate.ai/sdk/v1/android/BusymateAI.kt
// Load https://your-assistant.busymate.ai/?channel=android in a WebView.
class AssistantBridge(private val webView: WebView) {
  @JavascriptInterface
  fun postMessage(raw: String) {
    val message = JSONObject(raw)
    if (message.optString("type") != "busymate.ai.v1.identity_request") return

    lifecycleScope.launch { // mint through YOUR authenticated API client
      val identity = api.mintLaunchIdentity()
      val response = JSONObject()
        .put("type", "busymate.ai.v1.identity")
        .put("token", identity.token)
        .put("nonce", identity.nonce)
      webView.evaluateJavascript(
        "window.postMessage(${JSONObject.quote(response.toString())}, '*')", null
      )
    }
  }
}

webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(AssistantBridge(webView), "BusymateAINative")
// SupportChatNative + support.chat.v1.* remain accepted for shipped apps.

06Десктоп

Тот же протокол подходит Electron, Tauri и нативным оболочкам

Используйте изолированный WebView, запретите новые окна внутри него, открывайте безопасные URL снаружи и отвечайте на запросы личности через привилегированный слой хоста.

assistant-renderer.tstypescript
import { mountBusymateAI } from "https://busymate.ai/sdk/v1/index.js";

// productAuth is a narrow preload/Tauri command bridge. It calls YOUR
// authenticated backend; no cookie, signing key, or refresh token is exposed
// to the renderer.
const assistant = await mountBusymateAI({
  assistant: "your-assistant",
  origin: "https://your-assistant.busymate.ai",
  label: "Ask bro",
  getIdentity: () => window.productAuth.mintAssistantIdentity(),
});

window.productAuth.onSessionChanged(() => assistant.refreshIdentity());
assistant.open();

// Electron main process (Tauri: use the equivalent shell/open allowlist):
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
  const target = new URL(url);
  if (target.protocol === "https:" || target.protocol === "http:") {
    void shell.openExternal(target.href);
  }
  return { action: "deny" };
});

07Инструменты MCP

Дайте ассистенту инструменты, не отдавая ему базу клиентов

Публикуйте полные схемы при обнаружении. Присвойте каждому инструменту один из трёх уровней доступа, отметьте изменения, требующие карточки подтверждения, и заново проверяйте права при каждом вызове.

Публичный

Может вызвать любой, кто пишет в чат. Цены, статус, возможности.

Идентифицированный

Требует авторизованного пользователя. Статус заказа, ответы по аккаунту.

Делегированный

Действует от имени авторизованного пользователя через актор-токен с ограниченными правами или собственное OAuth-разрешение пользователя.

Каждое изменение подтверждается на сервере. Инструменты, перечисленные в confirm_tools, останавливаются на карточке подтверждения перед выполнением; так же ведёт себя любое имя инструмента, которого ассистент не знает. Фреймы виджетов никогда не выполняют изменения.

oauth-requirements.txttext
MCP endpoint ............ https://YOUR-DOMAIN/mcp
RFC 8707 resource ....... https://YOUR-DOMAIN/mcp   (the token is audience-bound to this)
AS metadata (RFC 8414) .. https://YOUR-DOMAIN/.well-known/oauth-authorization-server
Resource meta (RFC 9728)  https://YOUR-DOMAIN/.well-known/oauth-protected-resource
Client registration ..... Dynamic (RFC 7591) — public client, no secret
PKCE .................... S256 required (RFC 7636)
Authorization response .. iss parameter checked (RFC 9207)
Grants .................. authorization_code + refresh_token
Delegated tools/call .... no bearer -> 401; user derived from the bearer,
                          NEVER from an account id in tool arguments
Customer experience ..... one separate Authorize account tools action is expected;
                          use signed_actor_token instead for automatic SSO
delegated-call.jsonjson
// POST https://YOUR-DOMAIN/mcp
// Authorization: Bearer <the per-user OAuth token bro obtained>
{
  "jsonrpc": "2.0",
  "id": 12,
  "method": "tools/call",
  "params": { "name": "get_my_account", "arguments": {} }
}
// Your server verifies the bearer, derives the user from its signed subject,
// and returns ONLY that user's data.

На чём говорит платформа

Один и тот же набор протоколов действует и когда платформа — клиент к вашему серверу, и когда она сервер для вашего MCP-клиента.

  • OAuth 2.1 с PKCE (S256); публичные клиенты регистрируются динамически (RFC 7591).
  • Обнаружение через метаданные сервера авторизации (RFC 8414) и метаданные защищённого ресурса (RFC 9728).
  • Гранты: authorization_code и refresh_token; ответ авторизации несёт издателя (RFC 9207).
  • Индикаторы ресурса (RFC 8707), когда платформа выступает OAuth-клиентом к вашему MCP-серверу.
  • Обмен токенами (RFC 8693) там, где он настроен.

08Передача оператору и аналитика

Задайте правила, когда подключается человек

Когда он передаёт диалог

Клиент просит человека, инструмент выдал ошибку, возврат превышает ваш лимит, встретилось ключевое слово или произошло любое событие, которое вы задали.

Как узнаёт ваша команда

Одни Входящие; назначение вручную, по очереди или тому, у кого меньше всего открытых чатов; оповещения в приложении, через Web Push, APNs и личный Telegram.

Ваша команда перехватывает

Наблюдайте за диалогом, подключайтесь с полным контекстом, отвечайте в том же чате; пока человек активен, ассистент ничего не меняет.

Аналитика

Повторяющиеся вопросы, пробелы в знаниях и неудачные сценарии, сгруппированные из реальных диалогов, со ссылками на подтверждения.

09Вход рабочего пространства

Оставьте себе свою базу пользователей и свою аутентификацию

Настройте URL входа и аккаунта рабочего пространства в Консоли. Гость, выбравший «Войти», покидает фрейм, проходит аутентификацию в вашем продукте и возвращается точно на исходный URL. Затем ваш бэкенд выдаёт одноразовый токен входа; Busymate AI никогда не получает ни пароль пользователя, ни ваш ключ подписи.

Изменение всегда затрагивает только собственные данные авторизованного пользователя. Инструменты аккаунта берут клиента из делегированного актора, заново проверяют права при каждом вызове и запрашивают подтверждение на тех изменениях, которые вы настроили.

10Чек-лист запуска

Чек-лист запуска в продакшен

  1. 01Настройте брендинг, SEO, веб-адрес и подсказки-вопросы.
  2. 02Зарегистрируйте свой вход: issuer, JWKS, audience, claims и срок жизни токена.
  3. 03Подключите MCP, задайте уровень доступа каждому инструменту, убедитесь, что чтения ограничены своими данными, а записи подтверждаются.
  4. 04Задайте правила передачи оператору, назначьте людей во Входящие, укажите часы работы и целевое время ответа.
  5. 05Проверьте гостей, авторизованных пользователей, выход и смену аккаунта, повторно отправленные токены и подменённый идентификатор рабочего пространства.
  6. 06Проверьте полностраничный чат, встраивание, iOS, Android, десктоп, доступность и внешние ссылки.
  7. 07Публикуйте только ту версию, которую проверили; следите за состоянием подключений и домена.

Готовы настроить своё рабочее пространство?

Страница интеграции в Консоли сама генерирует код и настройки для каждой площадки на основе ваших опубликованных настроек.

Открыть Консоль

MCP управления

Управляйте каждым рабочим пространством из любого MCP-клиента

Инструментов управления по MCP с OAuth 2.1 — 218. При первом подключении клиент открывает вход в браузере; ничего вставлять вручную не нужно.

Документы обнаружения

Метаданные сервера авторизации (RFC 8414)
https://busymate.ai/.well-known/oauth-authorization-server/mcp
Метаданные защищённого ресурса (RFC 9728)
https://busymate.ai/.well-known/oauth-protected-resource/mcp

Аккаунт, под которым вы вошли, определяет, какими рабочими пространствами клиент может управлять. Каждое изменение запрашивает подтверждение.

Your MCP clientcreate_artifactJSON-RPCcreated: truehttps://your-assistant.busymate.ai/artifact/…VISIBILITYSitemapGallery

Claude Code

claude-code.shbash
claude mcp add --transport http busymate-ai https://busymate.ai/mcp

Управляйте платформой из любого MCP-клиента

Cursor, Claude Desktop и любой клиент, читающий mcp.json, принимают эту запись; клиенты, которым нужен удалённый URL, берут точку выше.

mcp.jsonjson
{
  "mcpServers": {
    "platform-management": {
      "type": "http",
      "url": "https://busymate.ai/mcp"
    }
  }
}

Подключите первый инструмент уже сегодня

Зарегистрируйте свой MCP-сервер, выберите, кому доступен каждый инструмент, опубликуйте. Ассистент сможет им пользоваться в ту же минуту.