Desenvolvedores
MCP é o padrão aberto para conectar IA aos seus sistemas: registre seu servidor, escolha quem pode usar cada ferramenta, publique. Distribua o assistente na web, iOS, Android e desktop, e gerencie todo espaço de trabalho por qualquer cliente MCP.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpCaminhos de entrada
Uma tag de script em qualquer página que você permitir — o launcher e o chat para visitantes já funcionam.
Instale o pacote Swift versionado para identidade da conta, chat e permissão do microfone solicitada ao tocar no controle.
Instale a biblioteca Android versionada para identidade da conta, chat e permissão do microfone solicitada ao tocar no controle.
Seu próprio servidor MCP vira as ações do assistente, em nome de cada cliente.
Opere a plataforma a partir do Claude, do Claude Code, do Cursor ou de qualquer cliente MCP via OAuth.
WebMCP: suas páginas listam suas próprias ações como ferramentas — reservar, pedir, verificar um status — com confirmação integrada.
01Início rápido
Seu app continua sendo a fonte da verdade. O assistente recebe um token assinado de curta duração que diz quem está presente e chama suas ferramentas como um ator separado e com escopo próprio.
01
Guarda a identidade e os dados da conta; assina um token de dois minutos dizendo quem está conectado.
02
Aplica as configurações do seu espaço de trabalho: conteúdo, modelos, níveis de acesso das ferramentas, etapas de confirmação, transferência.
03
Responde as ferramentas para um usuário por vez, identificando-o pelo bearer token do login — nunca pelos argumentos da ferramenta.
7 formas de conectar, um assistente: Embed web · Clientes conectados · iOS · Android · Desktop · API REST · Seu servidor MCP
02Clientes conectados
Para que o assistente confie em quem está perguntando, um endpoint autenticado no seu backend assina um token de curta duração (um JWT ES256) com o seu id estável de cliente e alguns campos de exibição seguros.
O token tem vida útil de no máximo 120 segundos, contém um nonce única e jti, e identifica seu espaço de trabalho. A chave privada e sua sessão do produto nunca chegam ao navegador ou ao 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 });
});O token diz quem está ali, não o que essa pessoa pode fazer. Mantenha saldos, aparelhos, configurações e toda alteração atrás de ferramentas que respondem por um único usuário.
03Web e página inteira
O SDK pede a identidade ao seu backend quando precisa, funciona para visitantes, atualiza depois do login e do logout, abre links externos fora do frame e não precisa de cookies de terceiros.
<!-- 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 um usuário conectado abre o endereço do seu assistente ou o seu domínio próprio, passe o token de login no fragmento da URL. A página o apaga antes da troca.
// 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
Fixe a versão do pacote Swift. Conecte seu backend autenticado e adicione a permissão nativa do microfone com o guia da plataforma e o app de exemplo.
Instalação, configuração e demonstração de 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
Fixe a versão da biblioteca Android. Instale antes de carregar o chat e siga o guia sobre o ciclo de vida de Activity, o microfone e a recuperação do renderizador.
Instalação, configuração e demonstração de 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
Os SDKs oficiais para macOS, Windows, Electron, Linux e Tauri carregam o chat hospedado em um WebView isolado, respondem a pedidos de identidade pela camada privilegiada do host e abrem URLs seguras externamente. Fixe a versão do SDK e conecte o seu backend autenticado como no mobile.
Instalação, configuração e apps de exemplo para 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),
};07Ferramentas MCP
Publique schemas completos na descoberta. Dê a cada ferramenta um de três níveis de acesso, marque as alterações que precisam de cartão de confirmação e reconfira a autorização a cada chamada.
Qualquer pessoa no chat pode chamá-la. Preços, status, recursos.
Precisa de um usuário conectado. Status de pedido, respostas sobre a conta.
Age em nome do usuário conectado por um token de ator com escopo ou pela permissão OAuth do próprio usuário.
Toda alteração é confirmada no servidor. As ferramentas listadas em confirm_tools param em um cartão de confirmação antes de rodar; o mesmo vale para qualquer nome de ferramenta que o assistente não reconheça. Os frames de widget nunca executam uma alteração.
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.O mesmo conjunto de protocolos vale tanto quando a plataforma é o cliente do seu servidor quanto quando é o servidor do seu cliente MCP.
REST API e webhooks
Uma chave de API com escopo de espaço de trabalho autentica os endpoints REST e o servidor MCP. Liste e leia conversas, publique uma resposta do operador ou inscreva-se em um webhook em vez de fazer polling.
Crie uma chave em Console → API keys, depois envie-a como um token Bearer.
curl https://busymate.ai/api/v1/conversations \
-H "Authorization: Bearer bmai_sk_..."Referência completa: guia da API, webhooks, o documento OpenAPI.
08Transferência e insights
Um cliente pede uma pessoa, uma ferramenta falha, um reembolso passa do seu limite, uma palavra-chave aparece, ou qualquer evento que você definir.
Uma Caixa de entrada; atribuição à mão, por rodízio ou para quem tiver menos conversas abertas; alertas no app, por Web Push, APNs e Telegram pessoal.
Acompanhe a conversa, entre com o contexto, responda no mesmo chat; o assistente não faz alterações enquanto uma pessoa está ativa.
Perguntas repetidas, lacunas de conhecimento e caminhos que falharam, agrupados a partir de conversas reais com links para os registros.
09Login do espaço de trabalho
Configure no Console as URLs de login e de conta do espaço de trabalho. Um visitante que escolhe Entrar sai do frame, se autentica no seu produto e volta exatamente para a URL de origem. Seu backend então fornece um token de login de uso único; o Busymate AI nunca recebe a senha do usuário nem a sua chave de assinatura.
Uma alteração só toca nos dados do próprio usuário conectado. As ferramentas de conta identificam o cliente pelo ator delegado, reconferem a autorização a cada chamada e pedem confirmação nas alterações que você configurar.
10Checklist de lançamento
Pronto para montar seu espaço de trabalho?
A página de Integração do Console gera o código e as configurações de cada canal a partir das suas configurações publicadas.
WebMCP
WebMCP é o rascunho do W3C Web Machine Learning Community Group para páginas que listam suas próprias ações como ferramentas. Três formas de começar, da menor mudança ao controle mais completo.
book_appointment, check_order_status, start_return — uma ferramenta, uma tarefa.
“terça-feira que vem às dez”, “Express” — nunca um id interno que o assistente precise adivinhar.
Ofereça as ferramentas que esta página pode executar; cancele o registro delas quando a página mudar.
Uma reserva, um pagamento, uma devolução: sinalize, e o cliente confirma antes da execução.
A confirmação permanece com o cliente. As ferramentas são executadas no próprio navegador do visitante, na sua origem. Uma ferramenta marcada como consequente para em uma confirmação que o cliente vê; exposedTo limita quais origens podem sequer ver uma ferramenta.
A menor mudança: um nome e uma descrição de uma linha em um formulário que você já tem. O navegador o transforma em uma ferramenta; o formulário continua visível e funcionando para todos.
<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>Controle total: a API do navegador. Detecte o recurso — navegadores sem WebMCP simplesmente recebem a página como ela é hoje. Chrome 149 e Edge 150 a trazem como um origin trial; Firefox e Safari estão avaliando a 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"] });Onde o assistente já está instalado, uma chamada lista suas ferramentas para ele e para qualquer outro assistente que visitar — pela API do navegador onde ela existe, e pela própria ponte do assistente em todo o resto.
// 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(),
},
]);O guia de adoção e o prompt para copiar e colarA página em linguagem simples para sua equipe
MCP de gestão
Ferramentas de gerenciamento sobre MCP com OAuth 2.1. O cliente abre um navegador de login na primeira conexão; nada é colado.
https://busymate.ai/mcp| Cliente | Como se conecta |
|---|---|
| Claude | Adicione o conector a partir do diretório de conectores do Claude e faça login — nada para colar. |
| ChatGPT and Codex | O app está em análise pela OpenAI e ainda não está listado. |
| Claude Code (CLI) | Execute o comando abaixo — o navegador gerencia a autenticação OAuth. |
| Cursor | Cole a entrada mcp.json abaixo nas configurações MCP do Cursor. |
| Outros clientes MCP | Forneça ao cliente o endpoint abaixo; descoberta OAuth 2.1 + PKCE cuidam do resto. |
A conta com que você entra define quais espaços de trabalho o cliente pode gerenciar. Toda alteração pede confirmação.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpCursor, Claude Desktop e qualquer cliente que leia um mcp.json aceitam esta entrada; clientes que recebem uma URL remota usam o endpoint acima.
{
"mcpServers": {
"busymate-ai": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}Mudanças recentes
compilação 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.
compilação 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.
compilação 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.