Desenvolvedores
Dê ferramentas reais ao seu assistente de IA com MCP
Dê ferramentas reais ao assistente por meio do MCP, o padrão aberto para conectar IA aos seus sistemas. Publique na web, no iOS, no Android e no desktop. Gerencie tudo por qualquer cliente MCP.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpCaminhos de entrada
Escolha por onde começar
Adicione o assistente ao seu site
Uma tag de script em qualquer página que você permitir — o launcher e o chat para visitantes já funcionam.
Coloque suporte com IA dentro do seu app iOS
Carregue o chat em uma WKWebView; uma pequena ponte de três mensagens conecta seus usuários.
Coloque suporte com IA dentro do seu app Android
Carregue o chat em uma WebView; uma interface JavaScript conecta seus usuários.
Conecte seu servidor MCP como ferramentas
Seu próprio servidor MCP vira as ações do assistente, em nome de cada cliente.
Gerencie todo espaço de trabalho pelo terminal
Controle a plataforma pelo Claude Code, Cursor ou qualquer cliente MCP por OAuth.
Deixe agentes de IA lerem seu site
Um mapa em texto puro do seu site em /llms.txt, escrito para agentes de IA.
01Início rápido
Como tudo se encaixa
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
Seu app
Guarda a identidade e os dados da conta; assina um token de dois minutos dizendo quem está conectado.
02
O assistente
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
Seu servidor MCP
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
Reconheça o cliente que já está conectado
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 dura no máximo 120 segundos, carrega um nonce e um jti de uso único e nomeia o seu tenant. A chave privada e a sessão do seu produto nunca chegam ao navegador nem 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": "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 });
});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
Um script para um launcher embutido
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 "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>Abra o chat em página inteira no seu endereço
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
WKWebView com uma ponte de mensagens estreita
Permita apenas as três mensagens versionadas: pedido de identidade, resposta de identidade e URL externa. Gere a identidade pelo cliente de API autenticado que o app já tem.
// 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 com uma única interface permitida
Mantenha a navegação na origem de IA do tenant, envie links externos ao navegador do sistema e não exponha métodos nativos de uso geral.
// 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
O mesmo protocolo serve para Electron, Tauri e shells nativos
Use uma WebView isolada, bloqueie novas janelas na view, abra URLs seguras fora do app e responda aos pedidos de identidade pela camada privilegiada do host.
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" };
});07Ferramentas MCP
Dê ferramentas ao assistente sem dar a ele o seu banco de clientes
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.
Pública
Qualquer pessoa no chat pode chamá-la. Preços, status, recursos.
Identificada
Precisa de um usuário conectado. Status de pedido, respostas sobre a conta.
Delegada
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 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.O que a plataforma fala
O mesmo conjunto de protocolos vale tanto quando a plataforma é o cliente do seu servidor quanto quando é o servidor do seu cliente MCP.
- OAuth 2.1 com PKCE (S256); clientes públicos se registram dinamicamente (RFC 7591).
- Descoberta por metadados do servidor de autorização (RFC 8414) e metadados do recurso protegido (RFC 9728).
- Grants: authorization_code e refresh_token; a resposta de autorização carrega o issuer (RFC 9207).
- Resource indicators (RFC 8707) quando a plataforma é o cliente OAuth do seu servidor MCP.
- Troca de tokens (RFC 8693) onde estiver configurado.
08Transferência e insights
Defina as regras de quando uma pessoa entra
Quando ele transfere
Um cliente pede uma pessoa, uma ferramenta falha, um reembolso passa do seu limite, uma palavra-chave aparece, ou qualquer evento que você definir.
Como sua equipe é avisada
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.
Sua equipe assume
Acompanhe a conversa, entre com o contexto, responda no mesmo chat; o assistente não faz alterações enquanto uma pessoa está ativa.
Insights
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
Mantenha sua própria base de usuários e autenticação
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
Checklist de lançamento em produção
- 01Configure marca, SEO, seu endereço web e perguntas sugeridas.
- 02Registre seu login: issuer, JWKS, audience, claims e tempo de vida do token.
- 03Conecte o MCP, defina o nível de acesso de cada ferramenta, confirme que as leituras ficam no escopo do próprio usuário e que as escritas exigem confirmação.
- 04Defina as regras de transferência, escale a equipe na Caixa de entrada, ajuste horários e metas de resposta.
- 05Teste visitantes, usuários conectados, logout e troca de conta, tokens reaproveitados e um id de espaço de trabalho trocado.
- 06Confira o chat em página inteira, o embed, iOS, Android, desktop, acessibilidade e links externos.
- 07Publique só a versão que você conferiu; acompanhe a saúde da conexão e do domínio.
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.
MCP de gestão
Gerencie todo espaço de trabalho por qualquer cliente MCP
218 ferramentas de gestão por MCP com OAuth 2.1. O cliente abre um login no navegador na primeira conexão; nada é colado à mão.
Documentos de descoberta
- Endpoint MCP
- https://busymate.ai/mcp
- Metadados do servidor de autorização (RFC 8414)
- https://busymate.ai/.well-known/oauth-authorization-server/mcp
- Metadados do recurso protegido (RFC 9728)
- https://busymate.ai/.well-known/oauth-protected-resource/mcp
A conta com que você entra define quais espaços de trabalho o cliente pode gerenciar. Toda alteração pede confirmação.
Claude Code
claude mcp add --transport http busymate-ai https://busymate.ai/mcpGerencie a plataforma em qualquer cliente MCP
Cursor, Claude Desktop e qualquer cliente que leia um mcp.json aceitam esta entrada; clientes que recebem uma URL remota usam o endpoint acima.
{
"mcpServers": {
"platform-management": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}Mudanças recentes
O que foi lançado
Conecte sua primeira ferramenta hoje
Registre seu servidor MCP, escolha quem pode usar cada ferramenta, publique. O assistente já pode usá-la no mesmo minuto.