Sviluppatori
MCP è lo standard aperto per connettere l'AI ai suoi sistemi: registri il suo server, scelga chi può usare ogni strumento, pubblichi. Distribuisca l'assistente su web, iOS, Android e desktop, e gestisca ogni spazio di lavoro da qualsiasi client MCP.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpVie d’accesso
Un solo tag script su ogni pagina che autorizza — il launcher e la chat per ospiti funzionano subito.
Installa il pacchetto Swift versionato per l’identità dell’account, la chat e l’autorizzazione del microfono richiesta al tocco.
Installa la libreria Android versionata per l’identità dell’account, la chat e l’autorizzazione del microfono richiesta al tocco.
Il suo server MCP diventa le azioni dell’assistente, per conto di ogni cliente.
Guida la piattaforma da Claude, Claude Code, Cursor o qualsiasi client MCP tramite OAuth.
WebMCP: le sue pagine elencano i loro stessi strumenti — prenotare, ordinare, controllare uno stato — con conferma integrata.
01Avvio rapido
La sua applicazione resta la fonte di verità. L’assistente riceve un token firmato di breve durata che dice chi è presente e chiama i suoi strumenti come attore separato e limitato.
01
Custodisce identità e dati dell’account; firma un token di due minuti che dice chi è autenticato.
02
Applica le impostazioni del suo spazio di lavoro: contenuti, modelli, livelli di accesso degli strumenti, conferme, passaggio a un operatore.
03
Risponde agli strumenti per un utente alla volta, ricavando l’utente dal bearer token dell’accesso, mai dagli argomenti dello strumento.
7 modi per collegarsi, un solo assistente: Embed web · Clienti autenticati · iOS · Android · Desktop · REST API · Il suo server MCP
02Clienti autenticati
Perché l’assistente possa fidarsi di chi sta chiedendo, un endpoint autenticato del suo backend firma un token di breve durata (un JWT ES256) con l’id stabile del cliente e pochi campi sicuri da mostrare.
Il token vive al massimo 120 secondi, contiene un nonce monouso e jti, e nomina il suo spazio di lavoro. La chiave privata e la sua sessione di prodotto non raggiungono mai il browser o la 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 });
});Il token dice chi è presente, non cosa può fare. Tenga saldi, dispositivi, impostazioni e ogni modifica dietro strumenti che rispondono per un solo utente.
03Web e pagina intera
L’SDK chiede l’identità al suo backend quando serve, funziona per gli ospiti, si aggiorna dopo login e logout, apre i link esterni fuori dal frame e non richiede cookie di terze parti.
<!-- 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>Quando un utente autenticato apre l’indirizzo del suo assistente o il suo dominio personalizzato, passi il token di accesso nel frammento dell’URL. La pagina lo cancella prima dello scambio.
// 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
Fissa la versione del pacchetto Swift. Collega il tuo backend autenticato e aggiungi l’autorizzazione nativa del microfono con la guida della piattaforma e l’app di esempio.
Installazione, configurazione e demo 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
Fissa la versione della libreria Android. Installa prima di caricare la chat e segui la guida sul ciclo di vita di Activity, sul microfono e sul ripristino del renderer.
Installazione, configurazione e demo 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
Gli SDK ufficiali per macOS, Windows, Electron, Linux e Tauri caricano la chat ospitata in una WebView isolata, rispondono alle richieste di identità tramite il livello host privilegiato e aprono le URL sicure all’esterno. Fissa la versione dell’SDK e collega il tuo backend autenticato come su mobile.
Installazione, configurazione e app di esempio per 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),
};07Strumenti MCP
Pubblichi schemi completi in fase di discovery. Assegni a ogni strumento uno dei tre livelli di accesso, contrassegni le modifiche che richiedono una scheda di conferma e ricontrolli l’autorizzazione a ogni chiamata.
Può chiamarlo chiunque stia chattando. Prezzi, stato, funzioni.
Richiede un utente autenticato. Stato degli ordini, risposte sull’account.
Agisce per conto dell’utente autenticato tramite un token attore limitato o l’autorizzazione OAuth dell’utente stesso.
Ogni modifica viene confermata sul server. Gli strumenti elencati in confirm_tools si fermano su una scheda di conferma prima di essere eseguiti; lo stesso vale per ogni nome di strumento che l’assistente non riconosce. I frame dei widget non eseguono mai una modifica.
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.Lo stesso insieme di protocolli vale sia quando la piattaforma è il client verso il suo server, sia quando è il server per il suo client MCP.
REST API e webhook
Una chiave API con ambito dello spazio di lavoro autentica sia gli endpoint REST che il server MCP. Elenchi e legga le conversazioni, pubblichi una risposta dell'operatore oppure si abboni a un webhook invece di fare polling.
Crei una chiave in Console → Chiavi API, quindi la invii come token Bearer.
curl https://busymate.ai/api/v1/conversations \
-H "Authorization: Bearer bmai_sk_..."Riferimento completo: la guida API, webhook, il documento OpenAPI.
08Passaggio e analisi
Un cliente chiede una persona, uno strumento fallisce, un rimborso supera il suo limite, compare una parola chiave o si verifica un evento che definisce lei.
Un’unica Inbox; assegnazione a mano, a turno o a chi ha meno chat aperte; avvisi nell’app, via Web Push, APNs e Telegram personale.
Segue la conversazione, entra con tutto il contesto e risponde nella stessa chat; l’assistente non apporta modifiche mentre una persona è attiva.
Domande ricorrenti, conoscenze mancanti e percorsi falliti, raggruppati da conversazioni reali con i link alle evidenze.
09Accesso allo spazio di lavoro
Configuri nella Console gli URL di login e account dello spazio di lavoro. Un ospite che sceglie Accedi esce dal frame, si autentica sul suo prodotto e torna esattamente all’URL di partenza. Il suo backend fornisce poi un token di accesso monouso; Busymate AI non riceve mai la password dell’utente né la sua chiave di firma.
Una modifica tocca soltanto i dati dell’utente autenticato. Gli strumenti sull’account ricavano il cliente dall’attore delegato, ricontrollano l’autorizzazione a ogni chiamata e chiedono conferma per le modifiche che lei configura.
10Checklist di lancio
Pronto a configurare il suo spazio di lavoro?
La pagina Integrazione della Console genera codice e impostazioni per ogni canale a partire dalle impostazioni che ha pubblicato.
WebMCP
WebMCP è la bozza del W3C Web Machine Learning Community Group per pagine che elencano i loro stessi strumenti. Tre modi per iniziare, dal cambiamento più piccolo al controllo completo.
book_appointment, check_order_status, start_return — uno strumento, un compito.
«martedì prossimo alle dieci», «Express» — mai un id interno che l'assistente deve indovinare.
Offra gli strumenti che questa pagina può eseguire; cancelli la registrazione quando la pagina cambia.
Una prenotazione, un pagamento, un reso: contrassegnilo, e il cliente conferma prima che si esegua.
La conferma rimane con il cliente. Gli strumenti si eseguono nel browser del visitatore, sulla sua origine. Uno strumento che lei contrassegna come consequenziale si ferma a una conferma che il cliente vede; exposedTo limita quali origini possono vedere uno strumento.
Il cambiamento più piccolo: un nome e una descrizione di una riga su un modulo che già possiede. Il browser lo trasforma in uno strumento; il modulo rimane visibile e continua a funzionare per tutti.
<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>Controllo totale: l'API del browser. Rileva la funzionalità — i browser senza WebMCP semplicemente ricevono la pagina così com'è oggi. Chrome 149 e Edge 150 la portano come origin trial; Firefox e Safari stanno esaminando la proposta.
// 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"] });Dove l'assistente è già installato, una chiamata elenca i suoi strumenti per esso e per ogni altro assistente che visita — sull'API del browser dove esiste, e attraverso il bridge del suo assistente ovunque.
// 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(),
},
]);La guida all'adozione e il prompt pronto da copiareLa pagina in linguaggio semplice per il suo team
MCP di gestione
Strumenti di gestione su MCP con OAuth 2.1. Il client apre un accesso da browser alla prima connessione; niente viene incollato.
https://busymate.ai/mcp| Client | Come si connette |
|---|---|
| Claude | Aggiungi il connettore dalla directory dei connettori di Claude e accedi: niente da incollare. |
| ChatGPT and Codex | L’app è in revisione presso OpenAI e non è ancora elencata. |
| Claude Code (CLI) | Esegua il comando sottostante — il browser gestisce l'accesso OAuth. |
| Cursor | Incolla l'entry mcp.json sottostante nelle impostazioni MCP di Cursor. |
| Altri client MCP | Fornisca al client l'endpoint sottostante; OAuth 2.1 discovery + PKCE gestiscono il resto. |
L’account con cui accede decide quali spazi di lavoro il client può gestire. Ogni modifica chiede conferma.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpCursor, Claude Desktop e qualsiasi client che legge un mcp.json accettano questa voce; i client che accettano un URL remoto usano l’endpoint qui sopra.
{
"mcpServers": {
"busymate-ai": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}Ultime novità
compilazione 1624
G4: with a workspace's pii read masking ON, the Inbox thread translation and the email copilot (Console and MCP summarize_email_thread, suggest_email_reply, translate_email, and the Console copilot on an email conversation) now reads the mail thread masked, so drafts and summaries cannot quote raw card numbers, IBANs or national ids. With the setting off nothing changes.
compilazione 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.
compilazione 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.