Sviluppatori
Dia al suo assistente AI strumenti veri con MCP
Dia all’assistente strumenti veri tramite MCP, lo standard aperto per collegare l’AI ai suoi sistemi. Lo pubblichi su web, iOS, Android e desktop. Gestisca tutto da qualsiasi client MCP.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpVie d’accesso
Scelga da dove partire
Aggiunga l’assistente al suo sito
Un solo tag script su ogni pagina che autorizza — il launcher e la chat per ospiti funzionano subito.
Porti l’assistenza AI dentro la sua app iOS
Carichi la chat in una WKWebView; un piccolo bridge a tre messaggi autentica i suoi utenti.
Porti l’assistenza AI dentro la sua app Android
Carichi la chat in una WebView; una sola interfaccia JavaScript autentica i suoi utenti.
Colleghi il suo server MCP come strumenti
Il suo server MCP diventa le azioni dell’assistente, per conto di ogni cliente.
Gestisca ogni spazio di lavoro dal terminale
Guidi la piattaforma da Claude Code, Cursor o qualsiasi client MCP tramite OAuth.
Lasci che gli agenti AI leggano il suo sito
Una mappa testuale del suo sito su /llms.txt, scritta per gli agenti AI.
01Avvio rapido
Come si incastra tutto
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
La sua applicazione
Custodisce identità e dati dell’account; firma un token di due minuti che dice chi è autenticato.
02
L’assistente
Applica le impostazioni del suo spazio di lavoro: contenuti, modelli, livelli di accesso degli strumenti, conferme, passaggio a un operatore.
03
Il suo server MCP
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
Riconosca il cliente già autenticato
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 dura al massimo 120 secondi, porta un nonce e un jti monouso e indica il suo tenant. La chiave privata e la sessione del suo prodotto non arrivano mai al browser né alla 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": "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 });
});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
Un solo script per il launcher incorporato
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 "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>Apra la chat a pagina intera sul suo indirizzo
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
WKWebView con un bridge di messaggi ristretto
Consenta solo i tre messaggi con versione: richiesta di identità, risposta di identità e URL esterno. Generi l’identità tramite il client API autenticato che l’app già usa.
// 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 con una sola interfaccia autorizzata
Mantieni la navigazione nell’origine IA del tenant, apri i link esterni nel browser di sistema e non esporre metodi nativi generici.
// 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.06Desktop
Lo stesso protocollo funziona con Electron, Tauri e shell native
Usi una WebView isolata, blocchi le nuove finestre interne, apra gli URL sicuri all’esterno e risponda alle richieste di identità tramite il livello host privilegiato.
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" };
});07Strumenti MCP
Dia strumenti all’assistente senza dargli il suo database clienti
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.
Pubblico
Può chiamarlo chiunque stia chattando. Prezzi, stato, funzioni.
Identificato
Richiede un utente autenticato. Stato degli ordini, risposte sull’account.
Delegato
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 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.Cosa parla la piattaforma
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.
- OAuth 2.1 con PKCE (S256); i client pubblici si registrano dinamicamente (RFC 7591).
- Discovery tramite i metadati del server di autorizzazione (RFC 8414) e della risorsa protetta (RFC 9728).
- Grant: authorization_code e refresh_token; la risposta di autorizzazione porta l’issuer (RFC 9207).
- Resource indicator (RFC 8707) quando la piattaforma è il client OAuth verso il suo server MCP.
- Token exchange (RFC 8693) dove è configurato.
08Passaggio e analisi
Definisca quando interviene una persona
Quando passa la conversazione
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.
Come viene avvisato il suo team
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.
Il suo team prende il controllo
Segue la conversazione, entra con tutto il contesto e risponde nella stessa chat; l’assistente non apporta modifiche mentre una persona è attiva.
Analisi
Domande ricorrenti, conoscenze mancanti e percorsi falliti, raggruppati da conversazioni reali con i link alle evidenze.
09Accesso allo spazio di lavoro
Mantenga la sua base utenti e la sua autenticazione
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
Checklist di lancio in produzione
- 01Imposti marchio, SEO, indirizzo web e domande suggerite.
- 02Registri il suo accesso: issuer, JWKS, audience, claim e durata del token.
- 03Colleghi MCP, imposti il livello di accesso di ogni strumento, si accerti che le letture riguardino solo l’utente e che le modifiche richiedano conferma.
- 04Definisca le regole di passaggio, assegni il personale all’Inbox, imposti orari e obiettivi di risposta.
- 05Provi ospiti, utenti autenticati, logout e cambio account, token riutilizzati e uno scambio di id dello spazio di lavoro.
- 06Controlli la chat a pagina intera, l’embed, iOS, Android, desktop, accessibilità e link esterni.
- 07Pubblichi solo la versione che ha controllato; tenga d’occhio lo stato della connessione e del dominio.
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.
MCP di gestione
Gestisca ogni spazio di lavoro da qualsiasi client MCP
218 strumenti di gestione via MCP con OAuth 2.1. Al primo collegamento il client apre un accesso nel browser: non si incolla nulla.
Documenti di discovery
- Endpoint MCP
- https://busymate.ai/mcp
- Metadati del server di autorizzazione (RFC 8414)
- https://busymate.ai/.well-known/oauth-authorization-server/mcp
- Metadati della risorsa protetta (RFC 9728)
- https://busymate.ai/.well-known/oauth-protected-resource/mcp
L’account con cui accede decide quali spazi di lavoro il client può gestire. Ogni modifica chiede conferma.
Claude Code
claude mcp add --transport http busymate-ai https://busymate.ai/mcpGestisci la piattaforma da qualsiasi client MCP
Cursor, 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": {
"platform-management": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}Ultime novità
Cosa è stato rilasciato
Colleghi il suo primo strumento oggi
Registri il suo server MCP, scelga chi può usare ogni strumento, pubblichi. L’assistente può usarlo già nello stesso minuto.