تتيح أحداث MCP لخادم MCP الخاص بك إرسال التحديثات إلى ChatGPT فور حدوثها، بحيث لا يحتاج الوكيل إلى الاستعلام عنها. منذ يوم مطوري OpenAI في 29 سبتمبر 2026، يدعم ChatGPT مواصفات أحداث MCP المقترحة في الإصدار 2026-07-28 (MCP 2.0) على جميع الخطط. يضيف خادمك ثلاث طرق: events/list وevents/subscribe وevents/unsubscribe، ويعلن عن events في server/discover، ويتحقق من الاتصال العكسي، ويوقّع كل تسليم باستخدام Standard Webhooks HMAC. يقبل ChatGPT تسليم الويب هوك فقط.
يغطي هذا الدليل أشكال الرسائل، وقواعد الأمان المطلوبة، وطريقة اختبار الحلقة الكاملة. إذا كنت جديدًا على البروتوكول، ابدأ بـ بروتوكول MCP. ستستخدم Apidog لإرسال طلبات JSON-RPC ومحاكاة الاتصال العكسي.
نظرة سريعة على أحداث MCP
| العنصر | ما يتوقعه ChatGPT |
|---|---|
| البروتوكول | MCP 2.0، الإصدار 2026-07-28
|
| القدرة |
"events": {} في قدرات server/discover
|
| الأساليب |
events/list وevents/subscribe وevents/unsubscribe على نقطة النهاية نفسها التي توثق أدواتك |
| التسليم | عبر الويب هوك فقط: لا استقصاء، لا بث مباشر، ولا إشعارات gap أو terminated
|
| التوقيع | Standard Webhooks HMAC-SHA256 |
| الرؤوس |
webhook-id وwebhook-timestamp وwebhook-signature وX-MCP-Subscription-Id
|
| السر | قيمة whsec_ مع base64 يفك إلى 24–64 بايت، يوفرها ChatGPT |
| حد الحمولة | 256 كيلوبايت، أي 262,144 بايت، وحدث واحد لكل طلب |
| معرف الاشتراك | حتمي من المبدأ، وعنوان URL للاتصال العكسي، واسم الحدث، والوسائط |
| الاتصالات العكسية | HTTPS، وتحقق من التحدي، ولا عناوين خاصة أو توجيهات |
المصادر: دليل OpenAI MCP Events ومسودة تصميم MCP Events.
لماذا تستخدم الأحداث بدلًا من الاستقصاء؟
بدون الأحداث، يستدعي الوكيل أداة بشكل دوري لمقارنة النتائج. هذا يهدر الطلبات عند عدم وجود تغييرات ويؤخر الاستجابة عند حدوث تغيير. مع MCP Events، يرسل خادمك التحديث عندما يعرف أن سجلًا تغير.
هذه هي المقايضة التقليدية بين الويب هوكس والاستقصاء، ولكن داخل MCP.
أمثلة عملية:
- مراقبة مهام جديدة في لوحة مشروع.
- تحويل رسائل الأخطاء إلى مسودات طلبات سحب باستخدام
message.createdمع فلترchannel_id. - تطبيق تعليقات المراجعة على مستند باستخدام
comment.createdمع فلترdocument_id.
المواصفات من مجموعة عمل Triggers and Events التابعة لـ MCP في المستودع التجريبي، لذا ثبّت تنفيذك على الإصدار 2026-07-28. لمزيد من سياق الإطلاق، راجع مركز DevDay 2026.
1. أعلن عن دعم الأحداث
أضف events إلى قدرات استجابة server/discover:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": {
"tools": {},
"events": {}
}
}
}
بعد ذلك، نفّذ events/list. يجب أن يتضمن كل تعريف حدث:
namedescriptiondelivery: ["webhook"]-
inputSchemaلوسائط الاشتراك، مثلdocument_id -
payloadSchemaلكائنdataفي التسليم
استخدم أسماء مستقرة وأوصافًا واضحة. طبّق الفلاتر على الخادم، ولا تعرض أحداثًا لا يستطيع الحساب المصادق عليه رؤيتها.
2. نفّذ events/subscribe
عندما يطلب المستخدم من ChatGPT مراقبة مورد، يرسل ChatGPT طلبًا مثل التالي:
{
"jsonrpc": "2.0",
"id": 2,
"method": "events/subscribe",
"params": {
"name": "comment.created",
"arguments": {
"document_id": "doc_123"
},
"delivery": {
"mode": "webhook",
"url": "https://receiver.example.com/mcp-events/callback_123",
"secret": "whsec_<base64-encoded-signing-key>"
},
"cursor": null
}
}
قبل قبول الاشتراك:
- فوّض المستخدم للحدث والوسائط المطلوبة.
- تحقّق من الوسائط مقابل
inputSchema. - تحقّق من أن السر يبدأ بـ
whsec_وأن base64 يفك إلى 24–64 بايت. - تحقّق من عنوان الاتصال العكسي.
- خزّن المالك، والفلاتر، وعنوان URL، والسر، وتاريخ انتهاء الصلاحية.
ثم أعد استجابة الاشتراك:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"id": "sub_123",
"refreshBefore": "2026-10-02T12:00:00Z",
"cursor": null,
"truncated": false
}
}
قواعد اشتراك مهمة
-
معرفات حتمية: اشتق
idمن المبدأ المصادق عليه، وعنوان URL للاتصال العكسي، واسم الحدث، والوسائط. تقترح المسودة استخدام SHA-256 مقطوع لهذا المفتاح. - اشتراكات متطابقة كـ upsert: إذا وصل الاشتراك نفسه مرة أخرى، حدّث السجل الحالي بدل إنشاء سجل جديد. طبّع JSON قبل المقارنة حتى لا ينشئ ترتيب المفاتيح تكرارات.
-
التحديث قبل
refreshBefore: يعيد ChatGPT استدعاءevents/subscribeبنفس الهوية والمؤشر الأخير. أعد تاريخ انتهاء جديدًا. إذا وصل سر جديد، استبدله ووقّع بالمفتاحين لفترة قصيرة. -
الأحداث غير القابلة للإعادة: أعد
cursor: nullعندما لا تستطيع إعادة تشغيل أحداث سابقة.
3. تحقّق من الاتصال العكسي قبل إرسال البيانات
قبل إرسال بيانات التطبيق، أرسل طلب POST موقّعًا يتضمن تحديًا جديدًا، قصير العمر، وأحادي الاستخدام:
{
"type": "verification",
"challenge": "a-single-use-random-value"
}
استخدم webhook-id فريدًا، مثل msg_verification_123، وأرسل رؤوس التوقيع نفسها المستخدمة في تسليم الأحداث.
يجب أن يعيد ChatGPT:
{
"challenge": "a-single-use-random-value"
}
مع رمز 2xx.
بعد ذلك:
- قارن التحدي في وقت ثابت.
- فعّل الاشتراك فقط عند نجاح المقارنة.
- عند الفشل، أعد خطأ JSON-RPC برمز
-32015(CallbackEndpointError). - استخدم
data.reasonمثلchallenge_failedأوtimeout. - خزّن نتيجة تحقق ناجحة لكل مبدأ وعنوان URL لفترة محددة لتجنب إعادة التحدي عند التحديث.
هذا التحقق ضروري لأن المشترك هو من يقدم السر. بدونه، قد يوجّه شخص خادمك إلى عنوان ضحية لإغراقه بالطلبات.
طبّق هذه القيود على كل طلب صادر:
- HTTPS فقط.
- حل DNS والتحقق من عنوان IP وقت الاتصال.
- حظر العناوين المحلية والخاصة وغير العامة.
- لا تتبع إعادة التوجيه مطلقًا.
4. سلّم الأحداث ووقّعها
عند وقوع حدث يطابق اشتراكًا، أرسل كائن حدث واحد إلى عنوان URL للاتصال العكسي:
{
"eventId": "evt_456",
"name": "comment.created",
"timestamp": "2026-10-01T12:05:00Z",
"data": {
"document_id": "doc_123",
"comment_id": "comment_456",
"text": "Can we add the rollout dates to this section?",
"url": "https://docs.example.com/doc_123#comment_456"
},
"cursor": null
}
أرسل الرؤوس التالية:
Content-Type: application/json
webhook-id: evt_456
webhook-timestamp: <unix-seconds>
webhook-signature: v1,<base64-signature>
X-MCP-Subscription-Id: sub_123
نقاط تنفيذية مهمة:
- اجعل
webhook-idمساويًا لـeventId. - حوّل JSON إلى نص مرة واحدة فقط.
- وقّع البايتات نفسها التي سترسلها.
- لا تتجاوز 256 كيلوبايت.
- أرسل ملخصًا للسجلات الكبيرة، واعرض أداة قراءة للحصول على التفاصيل.
- تعامل مع النص الذي أنشأه المستخدم كبيانات، ولا تضع تعليمات نموذج داخل الحمولة.
- أعد محاولة الأخطاء العابرة بتراجع أسي محدود.
- حافظ على معرف الحدث عند إعادة المحاولة، لكن وقّع كل محاولة جديدة.
- لا تعد المحاولة لرمزي
410أو413. - صمّم أدوات الكتابة لتكون متطابقة لأن الأحداث قد تصل خارج الترتيب.
راجع دليل تصميم الويب هوكس الموثوق به لتفاصيل جداول إعادة المحاولة.
5. تحقّق من توقيع Standard Webhooks
وفقًا لمواصفات Standard Webhooks، المحتوى الموقّع هو:
${webhook-id}.${webhook-timestamp}.${body}
استخدم HMAC-SHA256 بالمفتاح الناتج من فك base64 للسر بعد إزالة بادئة whsec_.
قد يحتوي رأس webhook-signature على توقيع واحد أو أكثر مفصول بمسافات:
v1,<base64>
توصي مسودة MCP برفض الطوابع الزمنية الأقدم من خمس دقائق وإزالة التكرارات حسب webhook-id.
فيما يلي جهاز استقبال صارم للاختبارات المحلية باستخدام مكونات Node المدمجة فقط:
// receiver.mjs: جهاز استقبال ويب هوكس قياسي صارم للاختبارات المحلية (Node 18+)
import { createServer } from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";
const SECRET = process.env.WEBHOOK_SECRET; // whsec_...
const MAX_BYTES = 256 * 1024;
const TOLERANCE_S = 5 * 60;
const seen = new Set();
export function verify(raw, h, secret, now = Math.floor(Date.now() / 1000)) {
const id = h["webhook-id"];
const ts = h["webhook-timestamp"];
const sigs = h["webhook-signature"];
if (!id || !ts || !sigs) return false;
const t = Number(ts);
if (!Number.isInteger(t) || Math.abs(now - t) > TOLERANCE_S) {
return false;
}
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const expected = createHmac("sha256", key)
.update(`${id}.${ts}.`)
.update(raw)
.digest();
return sigs.split(" ").some((s) => {
const [version, b64] = s.split(",");
const got = Buffer.from(b64 ?? "", "base64");
return (
version === "v1" &&
got.length === expected.length &&
timingSafeEqual(got, expected)
);
});
}
if (SECRET) {
createServer((req, res) => {
const chunks = [];
let size = 0;
req.on("data", (chunk) => {
size += chunk.length;
if (size <= MAX_BYTES) chunks.push(chunk);
});
req.on("end", () => {
if (size > MAX_BYTES) {
return res.writeHead(413).end();
}
const raw = Buffer.concat(chunks);
if (!verify(raw, req.headers, SECRET)) {
return res.writeHead(401).end();
}
let body;
try {
body = JSON.parse(raw);
} catch {
return res.writeHead(400).end();
}
if (body.type === "verification") {
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ challenge: body.challenge }));
}
const id = req.headers["webhook-id"];
if (!seen.has(id)) {
seen.add(id);
console.log(
req.headers["x-mcp-subscription-id"],
body.name,
id
);
}
res.writeHead(200).end();
});
}).listen(8787);
}
شغّل جهاز الاستقبال:
WEBHOOK_SECRET=whsec_... node receiver.mjs
السلوك المتوقع:
-
413للحمولات الأكبر من 256 كيلوبايت. -
401للتوقيع غير الصحيح أو للطابع الزمني القديم. - إعادة التحدي في طلبات التحقق.
- تسجيل كل
webhook-idمرة واحدة فقط.
تم التحقق من verify() مقابل متجه اختبار التوقيع في مكتبة JavaScript الخاصة بـ Standard Webhooks. راجع أيضًا التحقق من توقيع الويب هوك.
لن يسمح خادمك بالتسليم إلى localhost افتراضيًا بسبب حظر العناوين الخاصة. تسمح المسودة بالأهداف غير العامة فقط عند إعداد صريح، مثل قائمة سماح مخصصة للمطورين أو نفق يعرض جهاز الاستقبال.
6. اختبر التدفق قبل توصيل ChatGPT
استخدم Apidog لاختبار أوضاع الفشل التي توثقها OpenAI.
أنشئ بيئة تحتوي على:
MCP_URL
MCP_TOKEN
CALLBACK_URL
WEBHOOK_SECRET
أرسل كل طلب JSON-RPC كـ POST إلى {{MCP_URL}} مع:
Authorization: Bearer {{MCP_TOKEN}}
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: <method-name>
استخدم الرؤوس المطلوبة في ربط Streamable HTTP.
يحتاج كل نص أيضًا إلى params._meta يتضمن:
io.modelcontextprotocol/protocolVersionio.modelcontextprotocol/clientCapabilities
راجع مواصفات MCP الأساسية. قد يؤدي غياب _meta إلى -32602، ما قد يجعل اختبارك ينجح لسبب خاطئ.
سيناريو الاختبار
-
الاكتشاف
- أرسل
server/discover. - تأكد من وجود
$.result.capabilities.events. - تأكد من أن
$.result.supportedVersionsيحتوي على2026-07-28. - أرسل
events/list. - تأكد من أن
deliveryلكل حدث يحتوي علىwebhook.
- أرسل
-
اشتراك متطابق
- أرسل طلب الاشتراك باستخدام
{{CALLBACK_URL}}و{{WEBHOOK_SECRET}}. - احفظ
$.result.idفي متغيرSUB_ID. - أعد إرسال الطلب دون تغيير.
- أعد إرساله بعد إعادة ترتيب مفاتيح
arguments. - تأكد من بقاء
$.result.idمساويًا لـ{{SUB_ID}}.
- أرسل طلب الاشتراك باستخدام
-
التحقق من الصحة
- أرسل سرًا يفك إلى أقل من 24 بايت.
- أرسل عنوان اتصال عكسي يبدأ بـ
http://. - اختبر عنوان IP خاصًا.
- يجب أن تفشل هذه الحالات بالرمز
-32602(InvalidParams).
-
تحدي التحقق
- أنشئ Mock Endpoint في Apidog يعيد:
{ "challenge": "wrong" }
- استخدم عنوان Mock السحابي كـ callback.
- تأكد من أن
$.error.codeهو-32015. - تأكد من أن
$.error.data.reasonهوchallenge_failed. - بعد ذلك، وجّه
CALLBACK_URLإلى جهاز الاستقبال المحلي وتأكد من نجاح الاشتراك.
-
حمولة تتجاوز الحد
- شغّل حدثًا يتجاوز نصه 262,144 بايت.
- يجب أن يرفضه المرسل.
- إذا وصل إلى جهاز الاستقبال، يجب أن يعيد
413. - تأكد من ظهور محاولة واحدة في سجل الخادم، من دون إعادة محاولة.
-
إعادة التشغيل والعبث
- انسخ رؤوس ونص تسليم موقّع إلى طلب جديد في Apidog.
- أعد الإرسال فورًا: يجب أن تحصل على
200من دون سجل ثانٍ. - غيّر بايتًا واحدًا في النص: يجب أن تحصل على
401. - أعد الإرسال بعد أكثر من خمس دقائق: يجب أن تحصل على
401.
احفظ هذه الخطوات كسيناريو وشغّله في CI عبر واجهة سطر أوامر Apidog. راجع دليل اختبار خادم MCP لاختبار استدعاءات الأدوات، وكيفية اختبار الويب هوكس لتفاصيل أجهزة الاستقبال.
الأسئلة الشائعة
ما هي أحداث MCP؟
هي امتداد تجريبي لـ MCP يسمح للخادم بدفع إشعارات الأحداث إلى العميل بدلًا من الاستقصاء. يدعم ChatGPT وضع الويب هوك في الإصدار 2026-07-28.
هل يدعم ChatGPT الاستقصاء أو البث المباشر لأحداث MCP؟
لا. يدعم ChatGPT تسليم الويب هوك والتحقق من الاتصال العكسي فقط. الاستقصاء والبث المباشر وإشعارات gap وterminated غير مدعومة.
ما خطط ChatGPT التي تحصل على أحداث MCP؟
يشير ملخص يوم مطوري OpenAI إلى أن الميزة متاحة لجميع الخطط.
من ينشئ سر التوقيع؟
المشترك. يرسل ChatGPT سر whsec_ في delivery.secret، ويتحقق خادمك منه ويخزنه ويستخدمه للتوقيع، ولا ينشئ سرًا بديلًا.
ما الفرق بين MCP Events وAgents API؟
تدفع أحداث MCP البيانات من خادمك إلى ChatGPT. أما واجهة برمجة تطبيقات وكلاء OpenAI فتشغّل الوكلاء الذين تبنيهم وتعرض تقدمهم عبر البث المباشر أو الويب هوكس.
الخطوة التالية
ابدأ بأصغر تنفيذ ممكن:
- أضف
"events": {}إلى خادم MCP واحد. - اعرض حدثًا واحدًا مع فلتر واحد.
- نفّذ التحقق من callback وتوقيع Standard Webhooks.
- شغّل الاختبارات الستة أمام جهاز استقبال محلي.
- بعد نجاحها، صِل الخادم بـ ChatGPT ونفّذ قائمة دورة الحياة الخاصة بـ OpenAI.
نزّل Apidog واحتفظ بهذه الفحوصات كسيناريو يعمل مع كل التزام.
Top comments (0)