Dezvoltatori
MCP este standardul deschis pentru conectarea AI la sistemele tale: înregistrează-ți serverul, alege cine poate folosi fiecare instrument, publică. Lansează asistentul pe web, iOS, Android și desktop și administrează fiecare spațiu de lucru din orice client MCP.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpCăi de intrare
Un singur script tag pe orice pagină permiteți — launcherul și chatul pentru vizitatori funcționează imediat.
Instalează pachetul Swift versionat pentru identitatea contului, chat și permisiunea pentru microfon cerută la atingerea controlului.
Instalează biblioteca Android versionată pentru identitatea contului, chat și permisiunea pentru microfon cerută la atingerea controlului.
Propriul tău server MCP devine acțiunile asistentului, în numele fiecărui client.
Controlează platforma din Claude, Claude Code, Cursor sau orice client MCP prin OAuth.
WebMCP: paginile tale își listează propriile acțiuni ca instrumente — rezervare, comandă, verificare de status — cu confirmare încorporată.
01Start rapid
Aplicația ta rămâne sursa de adevăr. Asistentul primește un token semnat, de scurtă durată, care spune cine este prezent, și îți apelează instrumentele ca actor separat, cu drepturi limitate.
01
Păstrează identitatea și datele de cont; semnează un token de două minute care spune cine este autentificat.
02
Aplică setările spațiului tău de lucru: conținut, modele, niveluri de acces la instrumente, pași de confirmare, preluare.
03
Răspunde la instrumente pentru un singur utilizator o dată, luând utilizatorul din tokenul bearer al celui autentificat — niciodată din argumentele instrumentului.
7 moduri de conectare, un singur asistent: Embed web · Clienți autentificați · iOS · Android · Desktop · API REST · Serverul tău MCP
02Clienți autentificați
Ca asistentul să poată avea încredere în cine îl întreabă, un singur endpoint autentificat din backendul tău semnează un token de scurtă durată (un JWT ES256) cu id-ul stabil al clientului și câteva câmpuri sigure de afișare.
Tokenul trăiește cel mult 120 secunde, poartă un nonce unic și un jti și numește spațiul tău de lucru. Cheia privată și sesiunea produsului tău nu ajung niciodată în browser sau în chat.
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: process.env.BMAI_TENANT_ID, // the tenant id shown in Console → Identity
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(process.env.BMAI_ISSUER) // the Issuer you registered in Console → Identity
.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 });
});Tokenul spune cine este acolo, nu ce are voie să facă. Ține soldurile, dispozitivele, setările și fiecare modificare în spatele unor instrumente care răspund pentru un singur utilizator.
03Web și pagină întreagă
SDK-ul cere identitatea backendului tău atunci când are nevoie de una, funcționează pentru vizitatori, se reîmprospătează după autentificare și delogare, deschide linkurile externe în afara cadrului și nu are nevoie de cookie-uri terțe.
<!-- Floating "mate" 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, an access-token/session
// rotation, or an account switch — upgrades the live conversation in
// place, same thread. Do not send an identity postMessage directly.
window.assistantIdentityChanged = () =>
window.BusymateAI?.identityChanged?.() ?? Promise.resolve(false);
// Call after YOUR product completes logout. Without this the signed-out
// visitor keeps a verified chat and the previous transcript until the tab
// closes — a privacy defect, not a missing nicety.
window.assistantSignedOut = () =>
window.BusymateAI?.signedOut?.("signed_out");
</script>
<script
src="https://your-assistant.busymate.ai/embed/v1.js"
data-assistant="your-assistant"
data-label="Ask mate"
async></script>Când un utilizator autentificat deschide adresa asistentului tău sau domeniul tău propriu, transmite tokenul de autentificare în fragmentul URL-ului. Pagina îl șterge înainte de schimb.
// 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
Fixează versiunea pachetului Swift. Conectează backendul tău cu autentificare și adaugă permisiunea nativă pentru microfon folosind ghidul platformei și aplicația exemplu.
Instalare, configurare și demonstrație iOS
// Swift Package: https://github.com/serebano/busymate-ai-sdk-ios.git (exact 1.0.2)
// Full lifecycle + demo: https://busymate.ai/docs/guides/ios-sdk
import WebKit
import BusymateAI
@MainActor
func installAssistant(
on webView: WKWebView,
account: @escaping () -> String?,
mint: @escaping @MainActor (BusymateBridge.MintRequest) async throws -> BusymateBridge.MintToken?
) {
BusymateBridge.install(on: webView, config: .init(
assistant: "your-assistant",
origins: ["https://your-assistant.busymate.ai"],
account: account,
mint: mint
))
// mint calls YOUR authenticated backend with request.nonce; never cache tokens.
// Backend endpoint: https://YOUR-PRODUCT-DOMAIN/api/bmai/identity
webView.load(URLRequest(url: URL(string: "https://your-assistant.busymate.ai/?channel=ios")!))
}
// Microphone: declare NSMicrophoneUsageDescription; install and retain
// BusymateMicrophone before load, and forward WKUIDelegate media permission.
// Call BusymateBridge.accountChanged() on login/logout/account changes.05Android
Fixează versiunea bibliotecii Android. Instaleaz-o înainte să încarci chatul și urmează ghidul despre ciclul de viață Activity, microfon și recuperarea procesului de randare.
Instalare, configurare și demonstrație Android
// Gradle installation + lifecycle: https://busymate.ai/docs/guides/android-sdk
// Source: https://github.com/serebano/busymate-ai-sdk-android/tree/1.0.2
import android.webkit.WebView
import ai.busymate.bridge.BusymateBridge
fun installAssistant(
webView: WebView,
account: () -> String?,
mint: (Map<String, Any?>, (BusymateBridge.MintResult) -> Unit) -> Unit
) {
BusymateBridge.install(webView, BusymateBridge.Config(
assistant = "your-assistant",
origins = listOf("https://your-assistant.busymate.ai"),
account = account,
mint = mint
))
// mint calls YOUR authenticated backend with request["nonce"], then done(result).
// Backend endpoint: https://YOUR-PRODUCT-DOMAIN/api/bmai/identity
webView.loadUrl("https://your-assistant.busymate.ai/?channel=android")
}
// Microphone: declare INTERNET + RECORD_AUDIO; construct and retain
// BusymateMicrophone in Activity.onCreate BEFORE STARTED/loadUrl; forward
// WebChromeClient.onPermissionRequest. See the guide for renderer recovery.
// Call BusymateBridge.accountChanged() on login/logout/account changes.06Desktop
SDK-urile oficiale pentru macOS, Windows, Electron, Linux și Tauri încarcă chatul găzduit într-un WebView izolat, răspund cererilor de identitate prin stratul privilegiat al gazdei și deschid URL-urile sigure în exterior. Fixează versiunea SDK-ului și conectează backend-ul tău autentificat ca pe mobil.
Instalare, configurare și aplicații exemplu pentru desktop
// Official desktop SDK for your platform — macOS, Windows, Electron, Linux or Tauri:
// https://github.com/serebano/busymate-ai-sdk-electron (and -macos, -windows, -linux, -tauri).
// Every SDK takes these settings and installs the identity bridge before the first load.
const chatOptions = {
url: "https://your-assistant.busymate.ai/?channel=desktop_sdk", // the window's top-level page
assistant: "your-assistant",
account: () => productAuth.currentUserId(), // null when signed out
// YOUR authenticated backend signs { token, nonce } for request.nonce.
mint: (request) => productAuth.mintAssistantIdentity(request),
};07Instrumente MCP
Publică scheme complete la descoperire. Dă fiecărui instrument unul dintre cele trei niveluri de acces, marchează modificările care au nevoie de un card de confirmare și reverifică autorizarea la fiecare apel.
Oricine conversează îl poate apela. Prețuri, stare, funcții.
Necesită un utilizator autentificat. Starea comenzilor, răspunsuri despre cont.
Acționează în numele utilizatorului autentificat, printr-un token de actor cu drepturi limitate sau prin permisiunea OAuth a utilizatorului însuși.
Fiecare modificare este confirmată pe server. Instrumentele pe care le enumerați în confirm_tools se opresc la un card de confirmare înainte să ruleze; la fel și orice nume de instrument pe care asistentul nu îl recunoaște. Cadrele widget nu execută niciodată o modificare.
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 mate 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.Același set de protocoale se aplică indiferent dacă platforma este clientul serverului tău sau serverul clientului tău MCP.
REST API și webhook-uri
O singură cheie API, limitată la spațiul de lucru, autentifică atât endpointurile REST, cât și serverul MCP. Listează și citește conversații, postează un răspuns de operator sau abonează-te la un webhook în loc să interoghezi periodic.
Creează o cheie în Consolă → API keys, apoi trimite-o ca token Bearer.
curl https://busymate.ai/api/v1/conversations \
-H "Authorization: Bearer bmai_sk_..."Referință completă: ghidul API, webhook-uri, documentul OpenAPI.
08Preluare și analize
Un client cere un om, un instrument eșuează, o rambursare depășește limita ta, apare un cuvânt-cheie sau orice eveniment definit de tine.
Un singur Inbox; alocare manuală, pe rând sau către cel cu cele mai puține discuții deschise; alerte în aplicație, prin Web Push, APNs și Telegram personal.
Urmărește conversația, intră cu tot contextul, răspunde în același chat; asistentul nu face nicio modificare cât timp un om este activ.
Întrebări repetate, cunoștințe lipsă și trasee eșuate, grupate din conversații reale, cu linkuri către dovezi.
09Login pentru spațiul de lucru
Configurează URL-urile de login și de cont ale spațiului de lucru în Consolă. Un vizitator care alege Autentificare părăsește cadrul, se autentifică în produsul tău și revine exact la URL-ul de plecare. Backendul tău furnizează apoi un token de autentificare de unică folosință; Busymate AI nu primește niciodată parola utilizatorului și nici cheia ta de semnare.
O modificare atinge întotdeauna doar datele proprii ale utilizatorului autentificat. Instrumentele de cont preiau clientul din actorul delegat, reverifică autorizarea la fiecare apel și cer confirmare pentru modificările pe care le configurezi.
10Listă de verificare la lansare
Gata să-ți configurezi spațiul de lucru?
Pagina Integrare din Consolă generează codul și setările pentru fiecare suprafață pornind de la setările tale publicate.
WebMCP
WebMCP este proiectul draft al Grupului Comunității W3C Web Machine Learning pentru pagini care listează propriile acțiuni ca instrumente. Trei moduri de acces, de la schimbarea cea mai mică la controlul complet.
book_appointment, check_order_status, start_return — un instrument, o sarcină.
"marți viitoare la zece", "Express" — niciodată un id intern pe care asistentul trebuie să-l ghicească.
Oferă instrumentele pe care această pagină le poate rula; anulează-le înregistrarea când pagina merge mai departe.
O rezervare, o plată, o întoarcere: marcați-o și clientul confirmă înainte ca aceasta să se execute.
Confirmarea rămâne la client. Instrumentele rulează în browserul propriu al vizitatorului, pe originea ta. Un instrument pe care îl marchezi ca având consecințe se oprește la o confirmare pe care clientul o vede; exposedTo limitează ce origini pot vedea un instrument.
Cea mai mică schimbare: un nume și o descriere pe o linie într-un formular pe care îl ai deja. Browserul îl transformă în instrument; formularul rămâne vizibil și continuă să funcționeze pentru toți.
<form action="/book" toolname="book_appointment"
tooldescription="Book an appointment for a date, a time and a service."
toolautosubmit>
<input name="date" type="date" toolparamdescription="The day, e.g. next Tuesday">
<input name="time" type="time" toolparamdescription="The start time">
<select name="service" toolparamdescription="Which service to book">
<option>Consultation</option>
<option>Follow-up</option>
</select>
<button type="submit">Book</button>
</form>Control complet: API-ul browserului. Detectează funcția — browserele fără WebMCP primesc pur și simplu pagina așa cum este astăzi. Chrome 149 și Edge 150 o oferă ca origin trial; Firefox și Safari analizează propunerea.
// The browser API (draft). Load the polyfill BEFORE this file so every browser has it
// (it stands down where the browser is native):
// <script src="https://busymate.ai/webmcp/polyfill-1.0.1.js" integrity="sha384-pLZ5lO3gwZIn8Nim+hDrUQY+bvpliRtZhrS1O1OGRFB3lvoW0sZ71DUFHIk3m+DG" crossorigin="anonymous"></script>
const ctx = document.modelContext ?? navigator.modelContext;
if (!ctx) throw new Error("no WebMCP registry: the polyfill did not load");
ctx?.registerTool({
name: "start_return",
description: "Start a return for a delivered order. Asks the customer to confirm first.",
inputSchema: { type: "object", properties: { orderNumber: { type: "string", description: "The order number, as printed on the receipt" } }, required: ["orderNumber"] },
annotations: { consequentialHint: true },
execute: async ({ orderNumber }) => startReturn(orderNumber),
}, { exposedTo: ["self"] });Acolo unde asistentul este deja instalat, un singur apel îți listează instrumentele pentru el și pentru fiecare alt asistent care vizitează — pe API-ul browserului acolo unde există și prin propria punte a asistentului peste tot în rest.
// One call, both transports: the browser's own WebMCP where it exists,
// and the assistant's bridge everywhere else (Safari, Firefox, app WebViews).
BusymateAI.registerPageTools([
{
name: "check_order_status",
description: "Look up an order by its number and say where it is.",
inputSchema: {
type: "object",
properties: { orderNumber: { type: "string", description: "The order number, as printed on the receipt" } },
required: ["orderNumber"],
additionalProperties: false,
},
annotations: { readOnlyHint: true },
execute: async ({ orderNumber }) => (await fetch(`/api/orders/${orderNumber}`)).json(),
},
]);Ghidul de adoptare și promptul gata de copiatPagina în limbaj simplu pentru echipa ta
MCP de administrare
Instrumente de administrare peste MCP cu OAuth 2.1. Clientul deschide o pagină de conectare în browser la prima conexiune; nu se lipește nimic.
https://busymate.ai/mcp| Client | Cum se conectează |
|---|---|
| Claude | Adaugă conectorul din directorul de conectori al Claude și autentifică-te — nimic de lipit. |
| ChatGPT and Codex | Aplicația este în revizuire la OpenAI și nu este încă listată. |
| Claude Code (CLI) | Rulează comanda de mai jos — browserul se ocupă de autentificarea OAuth. |
| Cursor | Lipește intrarea mcp.json de mai jos în setările MCP din Cursor. |
| Alți clienți MCP | Dați clientului endpoint-ul de mai jos; descoperirea OAuth 2.1 + PKCE se ocupă de restul. |
Contul cu care te autentifici decide ce spații de lucru poate administra clientul. Fiecare modificare cere confirmare.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpCursor, Claude Desktop și orice client care citește un mcp.json acceptă această intrare; clienții care primesc un URL remote folosesc endpointul de mai sus.
{
"mcpServers": {
"busymate-ai": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}Ultimele modificări
compilare 1623
G4: with a workspace's pii read masking ON, the transcript Markdown export (Console and MCP) now masks the Handoff header row, and the MCP draft_tenant_support_reply copilot reads the conversation masked like the Console copilot does. With the setting off both are unchanged. MCP activity records name the workspace and the result size. Each call an AI assistant makes to Busymate AI is now recorded with the workspace it targeted and how big its answer was, in bytes and rows. When a ChatGPT or Claude view shows no data, the cause can be found from the activity record alone.
compilare 1622
The script tag now says, once in the browser console, which call replaces an old one. Identify and refreshIdentity on the embed script keep working. The first call of each prints one console note naming the call to use instead (identityChanged), so integrations can move to the same names as the Web SDK (getIdentity, identityChanged, signedOut, account). The old v1 client drop-ins under /sdk/v1 are served unchanged and now answer with a Deprecation header.
compilare 1621
WhatsApp, Messenger and Telegram customers can sign in with your own login. A workspace can now let its chat customers sign in through the workspace's own login page. The customer sends "sign in", opens a one-time link, signs in, and sends the 6-digit code shown back in the same chat. Account tools then run as that customer with no extra card. The feature is off until the workspace lists the channels in channels.customerSignIn. ChatGPT and Claude: a write now asks first. The first call shows a preview (the card with Apply) and changes nothing; the change happens only when you press Apply or say yes in the chat . Signed-out shoppers get a reply again on stores whose site says "nobody is signed in". A signed-out shopper on a Shopify storefront (and any site whose getIdentity returns nothing) pressed Send and the chat stayed on "Still working…".