Busymate AI

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/mcp
TERMINAL$claude mcp add --transport http busymate-ai https://busymate.ai/mcpconnectéJSON-RPC 2.0 · tools/call{ "jsonrpc": "2.0", "id": 12, "method": "tools/call", "params": { "name": "get_my_account", "arguments": {} } }{ "jsonrpc": "2.0", "id": 12, "result": { "content": [{ "type": "text", "text": "…" }] } }

Points 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é.

Vos clientsVotre site ou applicationAssistantVos systèmesVos réglagesappliqué automatiquement

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

YOUR HELP SITErobots-aware1–50 pagesIndexRetrieve for an answerwith a source

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.

Your backendThe browserThe assistantsigns the tokenthe key never leavescarries itchecks your JWKS120s maxchecked
server/bmai-identity.tstypescript
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.

index.htmlhtml
<!-- 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.

open-assistant.tstypescript
// 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.

AssistantBridge.swiftswift
// 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.

AssistantBridge.ktkotlin
// 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.

assistant-renderer.tstypescript
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.

oauth-requirements.txttext
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
delegated-call.jsonjson
// 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

  1. 01Configurez l’identité de marque, le SEO, votre adresse web et les questions suggérées.
  2. 02Enregistrez votre connexion : émetteur, JWKS, audience, claims et durée du jeton.
  3. 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.
  4. 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.
  5. 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.
  6. 06Contrôlez le chat en pleine page, l’intégration, iOS, Android, l’ordinateur, l’accessibilité et les liens externes.
  7. 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.

Ouvrir la Console

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.

Your MCP clientcreate_artifactJSON-RPCcreated: truehttps://your-assistant.busymate.ai/artifact/…VISIBILITYSitemapGallery

Claude Code

claude-code.shbash
claude mcp add --transport http busymate-ai https://busymate.ai/mcp

Gé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.

mcp.jsonjson
{
  "mcpServers": {
    "platform-management": {
      "type": "http",
      "url": "https://busymate.ai/mcp"
    }
  }
}

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.