Développeurs
MCP est la norme ouverte pour connecter l'IA à vos systèmes : enregistrez votre serveur, choisissez qui peut utiliser chaque outil, publiez. Déployez l'assistant sur le web, iOS, Android et ordinateur, et pilotez chaque espace de travail depuis n'importe quel client MCP.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpPoints d’entrée
Une balise de script sur les pages de votre choix — le lanceur et le chat invité fonctionnent tout de suite.
Installez le package Swift versionné pour l’identité du compte, le chat et l’autorisation du microphone demandée lorsque le client touche le contrôle.
Installez la bibliothèque Android versionnée pour l’identité du compte, le chat et l’autorisation du microphone demandée lorsque le client touche le contrôle.
Votre propre serveur MCP devient les actions de l’assistant, pour le compte de chaque client.
Pilotez la plateforme depuis Claude, Claude Code, Cursor ou tout client MCP via OAuth.
WebMCP : vos pages énumèrent leurs propres actions comme outils — réserver, commander, vérifier un statut — avec confirmation intégrée.
01Démarrage rapide
Votre application reste la source de vérité. L’assistant reçoit un jeton signé de courte durée qui dit qui est présent, et appelle vos outils en tant qu’acteur distinct et limité.
01
Détient l’identité et les données de compte ; signe un jeton de deux minutes qui dit qui est connecté.
02
Applique les réglages de votre espace de travail : contenus, modèles, niveaux d’accès des outils, étapes de confirmation, transfert.
03
Répond aux outils pour un utilisateur à la fois, en prenant l’utilisateur dans le jeton porteur de connexion — jamais dans les arguments de l’outil.
7 façons de vous connecter, un seul assistant : Intégration web · Clients connectés · iOS · Android · Ordinateur · API REST · Votre serveur MCP
02Clients connectés
Pour que l’assistant sache à qui il a affaire, un point d’accès authentifié de votre backend signe un jeton de courte durée (un JWT ES256) avec votre identifiant client stable et quelques champs d’affichage sans risque.
Le jeton vit au maximum 120 secondes, porte un nonce et un jti uniques, et nomme votre espace de travail. La clé privée et votre session produit ne passent jamais par le navigateur ou le 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 });
});Le jeton dit qui est présent, pas ce qu’il a le droit de faire. Gardez les soldes, les appareils, les réglages et chaque modification derrière des outils qui ne répondent que pour un seul utilisateur.
03Web et pleine page
Le SDK demande une identité à votre backend quand il en a besoin, fonctionne pour les invités, se rafraîchit après connexion et déconnexion, ouvre les liens externes hors du cadre, et ne nécessite aucun cookie tiers.
<!-- 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>Quand un utilisateur connecté ouvre l’adresse de votre assistant ou votre domaine personnalisé, passez le jeton de connexion dans le fragment de l’URL. La page l’efface avant l’échange.
// 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
Fixez la version du package Swift. Connectez votre backend authentifié et ajoutez l’autorisation native du microphone à l’aide du guide de la plateforme et de l’application exemple.
Installation, configuration et démo 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
Fixez la version de la bibliothèque Android. Installez-la avant de charger le chat et suivez le guide sur le cycle de vie de Activity, le microphone et la récupération du moteur de rendu.
Installation, configuration et démo 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.06Ordinateur
Les SDK officiels pour macOS, Windows, Electron, Linux et Tauri chargent le chat hébergé dans une WebView isolée, répondent aux demandes d’identité via la couche hôte privilégiée et ouvrent les URL sûres à l’extérieur. Fixez la version du SDK et connectez votre backend authentifié comme sur mobile.
Installation, configuration et applications d’exemple pour le bureau
// 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),
};07Outils MCP
Publiez des schémas complets à la découverte. Donnez à chaque outil l’un des trois niveaux d’accès, marquez les modifications qui nécessitent une carte de confirmation, et recontrôlez l’autorisation à chaque appel.
Toute personne qui discute peut l’appeler. Prix, état, fonctionnalités.
Nécessite un utilisateur connecté. Suivi de commande, réponses sur le compte.
Agit pour le compte de l’utilisateur connecté, via un jeton d’acteur limité ou l’autorisation OAuth de l’utilisateur lui-même.
Chaque modification est confirmée côté serveur. Les outils que vous listez dans confirm_tools s’arrêtent sur une carte de confirmation avant de s’exécuter ; de même pour tout nom d’outil que l’assistant ne reconnaît pas. Les cadres de widget n’exécutent jamais de modification.
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.Le même ensemble de protocoles s’applique, que la plateforme soit le client de votre serveur ou le serveur de votre client MCP.
API REST et webhooks
Une clé API limitée à un espace de travail authentifie à la fois les points de terminaison REST et le serveur MCP. Listez et lisez les conversations, publiez une réponse d'opérateur ou abonnez-vous à un webhook au lieu d'interroger.
Créez une clé dans Console → Clés API, puis envoyez-la en tant que jeton Bearer.
curl https://busymate.ai/api/v1/conversations \
-H "Authorization: Bearer bmai_sk_..."Référence complète : le guide de l'API, webhooks, le document OpenAPI.
08Transfert et analyses
Un client demande une personne, un outil échoue, un remboursement dépasse votre limite, un mot-clé apparaît, ou tout autre événement que vous définissez.
Une seule boîte de réception ; attribution à la main, chacun son tour ou à la personne ayant le moins de chats ouverts ; alertes dans l’application, par Web Push, APNs et Telegram personnel.
Suivez la conversation, rejoignez-la avec tout le contexte, répondez dans le même chat ; l’assistant ne modifie rien tant qu’une personne est active.
Questions récurrentes, connaissances manquantes et parcours en échec, regroupés à partir de conversations réelles, avec des liens vers les éléments d’origine.
09Connexion à l’espace de travail
Configurez les URL de connexion et de compte de l’espace de travail dans la Console. Un invité qui choisit Se connecter quitte le cadre, s’authentifie sur votre produit et revient exactement à l’URL de départ. Votre backend fournit alors un jeton de connexion à usage unique ; Busymate AI ne reçoit jamais le mot de passe de l’utilisateur ni votre clé de signature.
Une modification ne touche jamais que les données de l’utilisateur connecté lui-même. Les outils de compte prennent le client dans l’acteur délégué, recontrôlent l’autorisation à chaque appel et demandent une confirmation sur les modifications que vous définissez.
10Check-list de lancement
Prêt à configurer votre espace de travail ?
La page Intégration de la Console génère le code et les réglages de chaque surface à partir de vos réglages publiés.
WebMCP
WebMCP est le brouillon du W3C Web Machine Learning Community Group pour les pages qui énumèrent leurs propres actions comme outils. Trois points d'entrée, du plus petit changement au contrôle maximal.
book_appointment, check_order_status, start_return — un outil, un rôle.
« mardi prochain à dix heures », « Express » — jamais un ID interne que l'assistant doit deviner.
Proposez les outils que cette page peut exécuter ; désenregistrez-les quand la page change.
Une réservation, un paiement, un retour : signalez-le, et le client confirme avant exécution.
La confirmation reste avec le client. Les outils s'exécutent dans le navigateur du visiteur, sur votre domaine. Un outil que vous marquez comme conséquent s'arrête à une confirmation que le client voit ; exposedTo limite les domaines qui peuvent voir un outil.
Le plus petit changement : un nom et une description en une ligne sur un formulaire que vous avez déjà. Le navigateur le transforme en outil ; le formulaire reste visible et continue de fonctionner pour tout le monde.
<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>Contrôle total : l'API du navigateur. Détectez les fonctionnalités — les navigateurs sans WebMCP obtiennent simplement la page telle qu'elle est aujourd'hui. Chrome 149 et Edge 150 la proposent en essai d'origine ; Firefox et Safari examinent la proposition.
// 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"] });Là où l'assistant est déjà installé, un appel énumère vos outils pour lui et pour tout autre assistant qui visite — sur l'API du navigateur où elle existe, et via le propre pont de l'assistant partout ailleurs.
// 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(),
},
]);Le guide d'adoption et l'invite prête à copier-collerLa page en langage courant pour votre équipe
MCP de gestion
Outils de gestion via MCP avec OAuth 2.1. Le client ouvre une authentification dans le navigateur à la première connexion ; rien n'est collé.
https://busymate.ai/mcp| Client | Mode de connexion |
|---|---|
| Claude | Ajoutez le connecteur depuis le répertoire des connecteurs de Claude et connectez-vous : rien à coller. |
| ChatGPT and Codex | L’application est en cours d’examen chez OpenAI et n’est pas encore référencée. |
| Claude Code (CLI) | Exécutez la commande ci-dessous — le navigateur gère la connexion OAuth. |
| Cursor | Collez l'entrée mcp.json ci-dessous dans les paramètres MCP de Cursor. |
| Autres clients MCP | Donnez au client le point de terminaison ci-dessous ; OAuth 2.1 discovery + PKCE gèrent le reste. |
Le compte avec lequel vous vous connectez détermine les espaces de travail que le client peut gérer. Chaque modification demande une confirmation.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpCursor, Claude Desktop et tout client qui lit un mcp.json acceptent cette entrée ; les clients qui prennent une URL distante prennent le point d’accès ci-dessus.
{
"mcpServers": {
"busymate-ai": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}Derniers changements
version 1630
Busymate AI views in ChatGPT load their own data when ChatGPT doesn't deliver it. When ChatGPT runs several Busymate AI lookups at once, for example the conversations of five workspaces, the iPhone app often passed the result to only one of the views and the rest said "Couldn't load this result". A view for a read-only lookup now fetches its own data after 3 seconds when nothing arrived, using the same request. If that fails, it shows the real reason.
version 1629
Signed-in customers now get one consistent answer about account tools. Each connector now has one "customer identity".
version 1628
Write confirmation (preview first, then Apply or yes in chat) now applies only in the ChatGPT and Claude apps. Automations and other connected clients write directly again, and every automated caller fails loudly if it ever gets a preview instead of a write .