Développeurs
Donnez de vrais outils à votre assistant IA avec MCP
Donnez de vrais outils à l’assistant via MCP, la norme ouverte qui relie l’IA à vos systèmes. Déployez-le sur le web, iOS, Android et l’ordinateur. Pilotez le tout depuis n’importe quel client MCP.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpPoints d’entrée
Choisissez par où commencer
Ajoutez l’assistant à votre site web
Une balise de script sur les pages de votre choix — le lanceur et le chat invité fonctionnent tout de suite.
Mettez le support IA dans votre application iOS
Chargez le chat dans une WKWebView ; un petit pont à trois messages connecte vos utilisateurs.
Mettez le support IA dans votre application Android
Chargez le chat dans une WebView ; une seule interface JavaScript connecte vos utilisateurs.
Connectez votre serveur MCP comme outils
Votre propre serveur MCP devient les actions de l’assistant, pour le compte de chaque client.
Pilotez chaque espace de travail depuis votre terminal
Pilotez la plateforme depuis Claude Code, Cursor ou tout client MCP via OAuth.
Laissez les agents IA lire votre site
Une carte de votre site en texte brut sur /llms.txt, écrite pour les agents IA.
01Démarrage rapide
Comment tout s’assemble
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
Votre application
Détient l’identité et les données de compte ; signe un jeton de deux minutes qui dit qui est connecté.
02
L’assistant
Applique les réglages de votre espace de travail : contenus, modèles, niveaux d’accès des outils, étapes de confirmation, transfert.
03
Votre serveur MCP
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
Reconnaissez le client déjà connecté
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 120 secondes au maximum, porte un nonce et un jti à usage unique, et nomme votre espace de travail. La clé privée et la session de votre produit n’atteignent jamais le navigateur ni 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": "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 });
});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
Un script pour un lanceur intégré
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 "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>Ouvrez le chat pleine page à votre adresse
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
WKWebView avec un pont de messages restreint
N’autorisez que les trois messages versionnés : demande d’identité, réponse d’identité et URL externe. Générez l’identité via le client API authentifié existant de l’application.
// 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 avec une seule interface autorisée
Conservez la navigation sur l’origine IA de votre tenant, ouvrez les liens externes dans le navigateur système et n’exposez aucune méthode native générique.
// 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.06Ordinateur
Le même protocole convient à Electron, Tauri et aux enveloppes natives
Utilisez une WebView isolée, refusez les nouvelles fenêtres dans la vue, ouvrez les URL sûres à l’extérieur et répondez aux demandes d’identité via la couche hôte privilégiée.
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" };
});07Outils MCP
Donnez des outils à l’assistant sans lui donner votre base clients
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.
Public
Toute personne qui discute peut l’appeler. Prix, état, fonctionnalités.
Identifié
Nécessite un utilisateur connecté. Suivi de commande, réponses sur le compte.
Délégué
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 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.Ce que parle la plateforme
Le même ensemble de protocoles s’applique, que la plateforme soit le client de votre serveur ou le serveur de votre client MCP.
- OAuth 2.1 avec PKCE (S256) ; les clients publics s’enregistrent dynamiquement (RFC 7591).
- Découverte via les métadonnées du serveur d’autorisation (RFC 8414) et les métadonnées de ressource protégée (RFC 9728).
- Autorisations : authorization_code et refresh_token ; la réponse d’autorisation porte l’émetteur (RFC 9207).
- Indicateurs de ressource (RFC 8707) quand la plateforme est le client OAuth de votre serveur MCP.
- Échange de jetons (RFC 8693) là où c’est configuré.
08Transfert et analyses
Définissez les règles d’intervention d’une personne
Quand il transfère
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.
Comment votre équipe est prévenue
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.
Votre équipe prend le relais
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.
Analyses
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
Gardez votre propre base d’utilisateurs et votre authentification
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
Check-list de mise en production
- 01Configurez l’identité de marque, le SEO, votre adresse web et les questions suggérées.
- 02Enregistrez votre connexion : émetteur, JWKS, audience, claims et durée du jeton.
- 03Connectez MCP, définissez le niveau d’accès de chaque outil, assurez-vous que les lectures sont limitées à l’utilisateur et les écritures confirmées.
- 04Définissez les règles de transfert, affectez l’équipe à la boîte de réception, fixez les horaires et les objectifs de réponse.
- 05Testez les invités, les utilisateurs connectés, la déconnexion et le changement de compte, les jetons rejoués et un identifiant d’espace de travail interverti.
- 06Contrôlez le chat en pleine page, l’intégration, iOS, Android, l’ordinateur, l’accessibilité et les liens externes.
- 07Ne publiez que la version que vous avez contrôlée ; surveillez la santé des connexions et des domaines.
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.
MCP de gestion
Pilotez chaque espace de travail depuis n’importe quel client MCP
218 outils de gestion via MCP avec OAuth 2.1. Le client ouvre une connexion dans le navigateur à la première utilisation ; rien à copier-coller.
Documents de découverte
- Point d’accès MCP
- https://busymate.ai/mcp
- Métadonnées du serveur d’autorisation (RFC 8414)
- https://busymate.ai/.well-known/oauth-authorization-server/mcp
- Métadonnées de ressource protégée (RFC 9728)
- https://busymate.ai/.well-known/oauth-protected-resource/mcp
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 Code
claude mcp add --transport http busymate-ai https://busymate.ai/mcpGérez la plateforme depuis n’importe quel client MCP
Cursor, 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": {
"platform-management": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}Derniers changements
Ce qui a été publié
Connectez votre premier outil dès aujourd’hui
Enregistrez votre serveur MCP, choisissez qui peut utiliser chaque outil, publiez. L’assistant peut s’en servir dans la minute.