المطوّرون
MCP هو المعيار المفتوح لربط الذكاء الاصطناعي بأنظمتك: سجل خادمك، واختر من يمكنه استخدام كل أداة، ثم انشرها. وزع المساعد على الويب و iOS و Android وأجهزة سطح المكتب، وأدر كل مساحة عمل من أي عميل MCP.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpمداخل البدء
وسم script واحد على أي صفحة تسمح بها — ويعمل زر الدردشة ودردشة الزوار فورًا.
ثبّت حزمة Swift ذات إصدار محدد لهوية الحساب والمحادثة وإذن الميكروفون الذي يُطلب عند النقر على زر التحكم.
ثبّت مكتبة Android ذات إصدار محدد لهوية الحساب والمحادثة وإذن الميكروفون الذي يُطلب عند النقر على زر التحكم.
يصبح خادم MCP الخاص بك هو إجراءات المساعد، نيابةً عن كل عميل.
تحكّم في المنصة من Claude أو Claude Code أو Cursor أو أي عميل MCP عبر OAuth.
WebMCP: صفحاتك تحدد إجراءاتها الخاصة كأدوات — حجز، طلب، التحقق من الحالة — مع تأكيد مدمج.
01البدء السريع
يبقى تطبيقك مصدر الحقيقة. ويتلقى المساعد رمزًا موقّعًا قصير الأجل يبيّن من الحاضر، ثم يستدعي أدواتك بصفته فاعلًا منفصلًا محدود الصلاحيات.
01
يحتفظ بالهوية وبيانات الحساب؛ ويوقّع رمزًا صالحًا لدقيقتين يبيّن من سجّل الدخول.
02
يطبّق إعدادات مساحة عملك: المحتوى، والنماذج، ومستويات الوصول إلى الأدوات، وخطوات التأكيد، والتحويل.
03
يجيب عن الأدوات لمستخدم واحد في كل مرة، مستمدًّا هوية المستخدم من الرمز الحامل الموقّع — لا من وسائط الأداة أبدًا.
7 طرق للربط، ومساعد واحد: تضمين الويب · العملاء المسجّلون · iOS · Android · سطح المكتب · REST API · خادم MCP الخاص بك
02العملاء المسجّلون
لكي يثق المساعد بهوية السائل، توقّع نقطة وصول مصادَق عليها واحدة على خادمك رمزًا قصير الأجل (JWT بخوارزمية ES256) يحمل معرّف العميل الثابت لديك وبضعة حقول عرض آمنة.
التوكن موجود لمدة 120 ثانية على الأكثر، ويحمل nonce و jti لمرة واحدة، ويسمي مساحة عملك. لا يصل المفتاح الخاص وجلسة منتجك أبدًا إلى المتصفح أو الدردشة.
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 });
});الرمز يقول من الحاضر، لا ما يُسمح له بفعله. أبقِ الأرصدة والأجهزة والإعدادات وكل تغيير خلف أدوات لا تجيب إلا لمستخدم واحد.
03الويب والصفحة الكاملة
تطلب حزمة SDK الهوية من خادمك عند الحاجة، وتعمل مع الزوار، وتُحدَّث بعد تسجيل الدخول والخروج، وتفتح الروابط الخارجية خارج الإطار، ولا تحتاج إلى ملفات تعريف ارتباط من جهات خارجية.
<!-- 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>عندما يفتح مستخدم مسجّل عنوان مساعدك أو نطاقك المخصص، مرّر رمز تسجيل الدخول في جزء العنوان (fragment). وتمسحه الصفحة قبل التبادل.
// 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
ثبّت إصدار حزمة Swift على إصدار محدد. اربط خادمك الخلفي الذي يستخدم المصادقة، وأضف إذن الميكروفون من النظام باتباع دليل المنصة والتطبيق النموذجي.
تثبيت SDK لنظام 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
ثبّت إصدار مكتبة Android على إصدار محدد. ثبّتها قبل تحميل المحادثة واتبع دليل دورة حياة Activity والميكروفون واستعادة عملية العرض.
تثبيت SDK لنظام 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.06سطح المكتب
تحمّل حزم SDK الرسمية لـ macOS وWindows وElectron وLinux وTauri الدردشة المستضافة داخل WebView معزول، وتجيب عن طلبات الهوية عبر طبقة المضيف ذات الصلاحيات، وتفتح الروابط الآمنة خارج التطبيق. ثبّت إصدار SDK واربط خلفيتك الموثّقة كما في الهاتف.
تثبيت سطح المكتب وإعداده وتطبيقات الأمثلة
// 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),
};07أدوات MCP
انشر مخططات كاملة عند الاكتشاف. وامنح كل أداة أحد مستويات الوصول الثلاثة، وحدّد التغييرات التي تحتاج إلى بطاقة تأكيد، وأعد التحقق من الصلاحيات في كل استدعاء.
يجوز لأي شخص يدردش استدعاؤه. الأسعار، والحالة، والميزات.
يتطلب مستخدمًا مسجّلًا. حالة الطلبات، وإجابات الحساب.
يتصرف نيابةً عن المستخدم المسجّل عبر رمز فاعل محدود الصلاحيات أو عبر إذن OAuth الخاص بالمستخدم نفسه.
كل تغيير يُؤكَّد على الخادم. الأدوات التي تدرجها في confirm_tools تتوقف عند بطاقة تأكيد قبل تنفيذها؛ وكذلك أي اسم أداة لا يتعرّف عليه المساعد. ولا تنفّذ إطارات اللوحات أي تغيير إطلاقًا.
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.تنطبق مجموعة البروتوكولات نفسها سواء كانت المنصة عميلًا لخادمك أو خادمًا لعميل MCP لديك.
REST API والـ webhooks
مفتاح API واحد محدود لمساحة العمل يُصرّح نقاط نهاية REST وخادم MCP على حد سواء. اعرض المحادثات واقرأها، أنشئ رد من المشغِّل، أو اشترك في webhook بدلاً من الاستقصاء.
أنشئ مفتاحًا في Console → API keys، ثم أرسله كرمز Bearer.
curl https://busymate.ai/api/v1/conversations \
-H "Authorization: Bearer bmai_sk_..."المرجع الكامل: دليل API, دليل Webhook, مستند OpenAPI.
08التحويل والرؤى
عندما يطلب العميل التحدث إلى إنسان، أو تفشل أداة، أو يتجاوز مبلغ الاسترداد حدّك، أو تظهر كلمة مفتاحية، أو أي حدث تحدده أنت.
صندوق وارد واحد؛ والإسناد يدويًا أو بالتناوب أو لمن لديه أقل عدد من المحادثات المفتوحة؛ والتنبيهات داخل التطبيق وعبر Web Push وAPNs وTelegram الشخصي.
راقب المحادثة، وانضم إليها مع كامل السياق، ورد في الدردشة نفسها؛ ولا يجري المساعد أي تغييرات ما دام هناك إنسان نشط.
الأسئلة المتكررة، والمعرفة الناقصة، والمسارات الفاشلة، مجمّعة من محادثات حقيقية مع روابط إلى الأدلة.
09تسجيل الدخول لمساحة العمل
اضبط عناوين تسجيل الدخول والحساب لمساحة العمل في لوحة التحكم. فالزائر الذي يختار تسجيل الدخول يغادر الإطار، ويصادق على منتجك، ثم يعود إلى العنوان الأصلي نفسه بالضبط. بعدها يوفّر خادمك رمز تسجيل دخول لمرة واحدة؛ ولا يتلقى Busymate AI أبدًا كلمة مرور المستخدم ولا مفتاح التوقيع لديك.
لا يمسّ أي تغيير سوى بيانات المستخدم المسجّل نفسه. تأخذ أدوات الحساب هوية العميل من الفاعل المفوَّض، وتعيد التحقق من الصلاحيات في كل استدعاء، وتطلب تأكيدًا على التغييرات التي تحددها أنت.
10قائمة تحقق الإطلاق
هل أنت مستعد لإعداد مساحة عملك؟
تنشئ صفحة التكامل في لوحة التحكم الشيفرة والإعدادات لكل واجهة انطلاقًا من إعداداتك المنشورة.
WebMCP
WebMCP هي مسودة مجموعة W3C Web Machine Learning المجتمعية للصفحات التي تحدد إجراءاتها الخاصة كأدوات. ثلاث طرق، من أصغر تغيير إلى التحكم الكامل.
book_appointment, check_order_status, start_return — أداة واحدة، مهمة واحدة.
«الثلاثاء القادم الساعة العاشرة»، «Express» — أبداً معرّف داخلي عليه أن يخمّنه المساعد.
قدم الأدوات التي يمكن لهذه الصفحة تشغيلها؛ ألغِ تسجيلها عند الانتقال إلى صفحة أخرى.
حجز، دفع، إرجاع: علّمها، والعميل يؤكد قبل تشغيلها.
التأكيد يبقى مع العميل. تعمل الأدوات في متصفح الزائر الخاص به، على أصلك. الأداة التي تحددها كذات عواقب تتوقف عند تأكيد يراه العميل؛ exposedTo يحد من الأصول التي قد ترى الأداة على الإطلاق.
أصغر تغيير: اسم ووصف بسطر واحد على نموذج لديك بالفعل. المتصفح يحولها إلى أداة؛ النموذج يبقى مرئياً ويستمر في العمل للجميع.
<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>التحكم الكامل: API المتصفح. اكتشف الميزة — المتصفحات بدون WebMCP تحصل ببساطة على الصفحة كما هي اليوم. Chrome 149 و Edge 150 يحملانها كتجربة أصل؛ Firefox و Safari يراجعان الاقتراح.
// 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"] });حيث يكون المساعد مثبتاً بالفعل، استدعاء واحد يسرد أدواتك لها ولكل مساعد آخر يزور — على API المتصفح حيث يوجد، وعلى جسر المساعد الخاص في كل مكان آخر.
// 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(),
},
]);دليل التبني والموجه الجاهز للنسخ واللصقالصفحة باللغة البسيطة لفريقك
MCP الإداري
أدوات إدارة عبر MCP مع OAuth 2.1. يفتح العميل نافذة تسجيل دخول في المتصفح عند الاتصال الأول؛ لا يتم لصق أي شيء.
https://busymate.ai/mcp| العميل | طريقة الاتصال |
|---|---|
| Claude | أضف الموصّل من دليل موصّلات Claude وسجّل الدخول، ولا شيء يحتاج إلى اللصق. |
| ChatGPT and Codex | التطبيق قيد المراجعة لدى OpenAI ولم يُدرج بعد. |
| Claude Code (CLI) | شغّل الأمر أدناه — المتصفح يتعامل مع تسجيل دخول OAuth. |
| Cursor | الصق إدخال mcp.json أدناه في إعدادات MCP لدى Cursor. |
| عملاء MCP الآخرون | أعط العميل نقطة النهاية أدناه؛ اكتشاف OAuth 2.1 و PKCE يتعاملان مع الباقي. |
الحساب الذي تسجّل الدخول به هو ما يحدد مساحات العمل التي يجوز للعميل إدارتها. وكل تغيير يطلب تأكيدًا.
claude mcp add --transport http busymate-ai https://busymate.ai/mcpيقبل Cursor وClaude Desktop وأي عميل يقرأ ملف mcp.json هذا الإدخال؛ أما العملاء الذين يقبلون عنوانًا بعيدًا فيأخذون نقطة الوصول أعلاه.
{
"mcpServers": {
"busymate-ai": {
"type": "http",
"url": "https://busymate.ai/mcp"
}
}
}أحدث التغييرات
إصدار البناء 1629
Signed-in customers now get one consistent answer about account tools. Each connector now has one "customer identity".
إصدار البناء 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 .
إصدار البناء 1627
The channel sign-in card can now be translated in every supported language. The sign-in card customers see when they connect from a messaging channel was stuck in English because its translation group was never registered. It is now, so the 14 translations can be published.