Разработчикам
Дайте своему ИИ-ассистенту настоящие инструменты через MCP
Дайте ассистенту настоящие инструменты через MCP — открытый стандарт подключения ИИ к вашим системам. Запустите его в вебе, на iOS, Android и десктопе. Управляйте всем из любого MCP-клиента.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpТочки входа
Выберите, с чего начать
Добавьте ассистента на свой сайт
Один тег скрипта на любой разрешённой странице — лончер и гостевой чат работают сразу.
Встройте ИИ-поддержку в приложение для 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-сервер
02Авторизованные клиенты
Узнавайте клиента, который уже авторизован
Чтобы ассистент мог доверять тому, кто спрашивает, одна аутентифицированная точка на вашем бэкенде подписывает короткоживущий токен (JWT на ES256) с вашим постоянным идентификатором клиента и несколькими безопасными полями для отображения.
Токен живёт не дольше 120 секунд, несёт одноразовый nonce и jti и называет ваш тенант. Приватный ключ и сессия вашего продукта никогда не попадают ни в браузер, ни в чат.
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.
<!-- 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. Страница очищает его до обмена.
// 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-клиент приложения.
// 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-домена тенанта, открывайте внешние ссылки в системном браузере и не предоставляйте универсальные нативные методы.
// 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 снаружи и отвечайте на запросы личности через привилегированный слой хоста.
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, останавливаются на карточке подтверждения перед выполнением; так же ведёт себя любое имя инструмента, которого ассистент не знает. Фреймы виджетов никогда не выполняют изменения.
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// 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Чек-лист запуска
Чек-лист запуска в продакшен
- 01Настройте брендинг, SEO, веб-адрес и подсказки-вопросы.
- 02Зарегистрируйте свой вход: issuer, JWKS, audience, claims и срок жизни токена.
- 03Подключите MCP, задайте уровень доступа каждому инструменту, убедитесь, что чтения ограничены своими данными, а записи подтверждаются.
- 04Задайте правила передачи оператору, назначьте людей во Входящие, укажите часы работы и целевое время ответа.
- 05Проверьте гостей, авторизованных пользователей, выход и смену аккаунта, повторно отправленные токены и подменённый идентификатор рабочего пространства.
- 06Проверьте полностраничный чат, встраивание, iOS, Android, десктоп, доступность и внешние ссылки.
- 07Публикуйте только ту версию, которую проверили; следите за состоянием подключений и домена.
Готовы настроить своё рабочее пространство?
Страница интеграции в Консоли сама генерирует код и настройки для каждой площадки на основе ваших опубликованных настроек.
MCP управления
Управляйте каждым рабочим пространством из любого MCP-клиента
Инструментов управления по MCP с OAuth 2.1 — 218. При первом подключении клиент открывает вход в браузере; ничего вставлять вручную не нужно.
Документы обнаружения
- Точка MCP
- https://busymate.ai/mcp
- Метаданные сервера авторизации (RFC 8414)
- https://busymate.ai/.well-known/oauth-authorization-server/mcp
- Метаданные защищённого ресурса (RFC 9728)
- https://busymate.ai/.well-known/oauth-protected-resource/mcp
Аккаунт, под которым вы вошли, определяет, какими рабочими пространствами клиент может управлять. Каждое изменение запрашивает подтверждение.
Claude Code
claude mcp add --transport http busymate-ai https://busymate.ai/mcpУправляйте платформой из любого MCP-клиента
Cursor, Claude Desktop и любой клиент, читающий mcp.json, принимают эту запись; клиенты, которым нужен удалённый URL, берут точку выше.
{
"mcpServers": {
"platform-management": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}Последние изменения
Что вышло
Подключите первый инструмент уже сегодня
Зарегистрируйте свой MCP-сервер, выберите, кому доступен каждый инструмент, опубликуйте. Ассистент сможет им пользоваться в ту же минуту.