أصبح بإمكان مسؤولي Promo Core الآن إدارة محتوى مواقعهم بمجرد التحدث إلى Claude أو ChatGPT أو Cursor. لم يعودوا بحاجة إلى التنقل بين صفحات الـ Dashboard، ولا يحصل الـ AI أبداً على صلاحيات أكثر من صلاحيات المسؤول الذي قام بربطه.
هذا المقال متابعة لعرضي التقديمي عن MCP. يشرح ما هو MCP، ولماذا أضفناه إلى Promo Core، وكيف يعمل من جهة المسؤول ومن جهة الـ Backend، وكيف نحافظ على أمانه. وفي النهاية أضفت بعض الدروس التي تعلمناها أثناء بنائه ولم يتسع لها وقت العرض.
MCP (Model Context Protocol) هو معيار مفتوح يتيح لتطبيقات الذكاء الاصطناعي (AI) استخدام أنظمة خارجية على شكل Tools. أطلقته Anthropic في نوفمبر 2024، وأصبح اليوم مدعوماً في Claude وChatGPT وCursor وVS Code.
الـ Model وحده لا يستطيع سوى كتابة النصوص. يمكنه أن يكتب لك مسودة مقال، لكن عليك نسخها إلى الـ Dashboard بنفسك. أما مع MCP فيستطيع الـ AI تنفيذ إجراءات حقيقية، مثل إنشاء ذلك المقال كمسودة مباشرة في الـ CMS.
قبل MCP كان لكل تطبيق AI نظام Plugins خاص به، لذلك كان لا بد من ربط كل Tool مع كل تطبيق على حدة. أما مع معيار موحد، فنكتب Server واحداً يعمل مع كل التطبيقات التي تدعم MCP. ولهذا يُشبَّه كثيراً بمنفذ USB-C للـ AI.
هناك ثلاثة أطراف:
لكل Tool اسم (مثل create_article) ووصف بلغة بسيطة والمدخلات التي يحتاجها. الـ Model لا يرى الكود أبداً، بل يقرأ هذه الأوصاف فقط ويختار الـ Tool المناسب، لذلك فإن كتابة أوصاف جيدة جزء كبير من العمل.
يُستخدم MCP في مجالات أوسع بكثير من إدارة المحتوى: يربطه المطورون بالـ Repositories والـ CI، وتربطه الشركات بـ Jira وSlack وNotion، وتستخدمه فرق الدعم لجلب الـ Tickets وصياغة الردود. أما حالتنا فهي إدارة المحتوى.
أعطينا الـ CMS واجهة جديدة، لا Backend جديداً. وكانت هناك أربعة أسباب لذلك:
يستغرق الربط حوالي دقيقة ويتم مرة واحدة فقط. بعدها يكفي أن يتحدث المسؤول مع الـ AI.
/mcp، ولا يمكن استخدامه لتسجيل الدخول إلى الـ Dashboard، وتنتهي صلاحيته ويتجدد تلقائياً.إليك كيف يبدو ذلك عملياً:
| ما يكتبه المسؤول | الـ Tool الذي يستدعيه الـ AI |
|---|---|
| "اعرض المقالات المحفوظة كمسودات" | list_articles |
| "أنشئ سؤالاً شائعاً عن الأسعار" | create_faq |
| "ارفع هذه الصورة إلى الـ Media" | upload_media |
| "اعرض الإجابات المرسلة على النموذج رقم 7" | list_form_submissions |
| "انشر المشروع رقم 12" | publish_project |
| "عدّل عنوان صفحة About" | update_page |
لا يحتاج المسؤول إلى معرفة أسماء هذه الـ Tools. يُنشأ المحتوى الجديد كمسودة (Draft) بشكل افتراضي، لذلك يراجعه شخص قبل نشره، ويبقى النشر دائماً خطوة منفصلة وصريحة.
طبقة MCP هي باب جديد إلى الـ NestJS Backend الحالي، وليست Backend ثانياً. ينتهي كل من الـ Dashboard والـ AI إلى استدعاء نفس الـ Service Methods، بنفس الـ Validation ونفس فحوصات الصلاحيات. الفرق فقط في المدخل (/api/admin/... مع Dashboard Token، أو /mcp مع MCP Token)، وفي من يقرر ما الذي يُستدعى: شخص يضغط الأزرار، أو Model يقرأ قائمة الـ Tools.
/api/admin/... مع Dashboard Token ← الـ Validation وفحص الصلاحيات.POST /mcp مع MCP Token يتحقق منه McpAuthGuard ← createMcpServer(userId) مع الـ Tools التي يسمح بها دور المستخدم فقط ← الـ Validation وفحص الصلاحيات.articleService.create()) ← Prisma ← PostgreSQL.الخطوة الإضافية الوحيدة في مسار الـ AI هي إنشاء MCP Server خاص بكل مسؤول. ومن فحص الصلاحيات فما دون، ينفذ المساران نفس الكود.
يمر استدعاء الـ Tool الواحد بخمس خطوات:
POST /mcp. كل شيء يمر عبر Endpoint واحد، مبني باستخدام الـ MCP TypeScript SDK الرسمي مع Streamable HTTP Transport. واستدعاء الـ Tool هو مجرد HTTP POST عادي يحمل JSON-RPC Body.McpAuthGuard. تتحقق Passport Strategy مخصصة من الـ Token: يجب أن يكون الـ Audience هو رابط الـ MCP الخاص بنا، وأن يتضمن الـ Scope القيمة mcp، وأن يطابق mcpTokenVersion القيمة الحالية للمستخدم في قاعدة البيانات.createMcpServer(userId). ننشئ MCP Server جديداً لهذا المسؤول، ونسجل فيه فقط الـ Tools التي يسمح بها دوره. وإذا لم تكن لديه صلاحية الحذف، فلا تُسجَّل Tools الحذف أصلاً، ولا يعرف الـ AI بوجودها.permissionService.assert() من الصلاحية المطلوبة، مثل CREATE_ARTICLE.articleService.create()، والتي تكتب في PostgreSQL عبر Prisma.كان هذا أكثر سؤال طُرح بعد العرض، وهناك ثلاثة أسباب:
userId الذي أُنشئ به، فلا يمكن أن يتسرب أي شيء بين المستخدمين.sessionIdGenerator: undefined). وفي هذا الوضع يتوقع الـ SDK إنشاء Server جديد لكل Request، لأن عميلين قد يرسلان نفس الـ JSON-RPC Message ID في الوقت نفسه، فتتعارض الرسائل على Server مشترك.إنشاء الـ Server عملية خفيفة، لأن تسجيل الـ Tools ينشئ Objects في الذاكرة فقط. أما التكلفة الحقيقية فهي بعض الاستعلامات من قاعدة البيانات لفحص الصلاحيات، وهذا مقبول تماماً بحجم استخدام الـ AI الحالي.
لم نكتب كل Tool يدوياً. كل Content Module موصوف بـ Config Object واحد، ويحوّله Registrar عام إلى ما يصل إلى سبعة Tools: list وget وcreate وupdate وpublish وunpublish وdelete.
{
resource: 'article',
permissions: { view, create, update, delete },
service: articleService,
createSchema, updateSchema,
publishField: 'published',
}هناك 26 Module مُعرّفة بهذه الطريقة، ما ينتج ما يصل إلى 174 Tool. وهناك حوالي 90 Tool أخرى مكتوبة يدوياً للعمليات التي ليست مجرد CRUD: رفع الـ Media، وإصدارات الـ Forms، والـ Email Templates، والـ Push Notifications، والأدوار والمسؤولين، والإعدادات، والـ Import/Export Jobs. وإضافة Content Module جديد إلى MCP تقتصر في الغالب على إضافة الـ Config والـ Schemas الخاصة به.
يحصل الـ AI على الصلاحيات التي يمنحها له المسؤول بالضبط، ويستطيع المسؤول سحبها في أي وقت. وهناك أربع ضمانات تحقق ذلك:
| الضمان | ماذا يعني | كيف يُطبَّق |
|---|---|---|
| الموافقة مطلوبة | لا يمكن لأي AI الاتصال قبل أن يسجل المسؤول الدخول ويوافق | OAuth 2.1 مع PKCE (S256 فقط)، وقائمة مسموحة من الـ Redirect URLs |
| MCP Token منفصل | لا يصل Dashboard Token إلى /mcp، ولا يستطيع MCP Token استدعاء الـ Dashboard API | تحمل الـ MCP Tokens قيمة Audience خاصة بها، وScope باسم mcp، وعداد إصدار خاصاً بها |
| صلاحيات الدور تُطبَّق | إذا كان دورك لا يسمح بإنشاء مقالات في الـ Dashboard، فلن يستطيع الـ AI ذلك أيضاً | كل Tool يفحص صلاحيته قبل التنفيذ، والـ Tools غير المسموح بها لا تُسجَّل أصلاً |
| سهولة إلغاء الوصول | ضغطة واحدة تفصل كل تطبيقات الـ AI دون تسجيل خروج المسؤول من الـ Dashboard | الإلغاء يزيد قيمة mcpTokenVersion، فتصبح كل الـ MCP Tokens السابقة لهذا المسؤول غير صالحة |
كما نضيف عبر الـ Seeder دوراً باسم AI Editor يملك صلاحيات العرض والإنشاء والتعديل على المحتوى، دون أي صلاحية حذف. ربط الـ AI بهذا الدور يعني أنه يستطيع كتابة المسودات وتعديلها، لكنه لا يستطيع حذف أي شيء. والـ Tools التي تحذف مُعلَّمة على أنها Destructive، حتى تتمكن تطبيقات الـ AI من طلب تأكيد المستخدم قبل استدعائها. كما تُسجَّل استدعاءات الـ Tools في الـ Logs مع اسم الـ Tool والـ User ID.
لا يتعامل الـ AI مع قاعدة البيانات مباشرة أبداً. كل ما يستطيعه هو استدعاء نفس الدوال التي تستدعيها واجهتنا، ويمر كل استدعاء عبر نفس الطبقات التي يمر بها Request الـ Dashboard:
لا تحتوي طبقة MCP على أي Raw SQL إطلاقاً، فلا يوجد Query String يمكن استهدافه بـ SQL Injection. كما أنها لا تحتوي على أي Business Logic خاص بها. وعندما يصلح أحدهم Bug أو يضيف قاعدة جديدة في أحد الـ Services، يحصل كل من الـ Dashboard والـ AI على التغيير تلقائياً.
لم يتسع لها وقت العرض، لكنها ستفيد كل من يبني MCP Server.
list_options first to get valid category types"، وعندها يبحث الـ AI عن القيم الصحيحة بنفسه بدلاً من التخمين.MCP جسر آمن بين الـ AI والـ CMS الخاص بنا. يعمل الـ AI بهوية المسؤول الذي ربطه، وبموافقته وصلاحياته، ويمكن سحب الوصول في أي وقت. وهو ليس CMS ثانياً، بل يستخدم نفس الـ Services ويتبع نفس القواعد.
لتجربته، افتح صفحة Connectors في Promo Core Dashboard واتبع الدليل الخاص بـ Claude أو ChatGPT أو Cursor. ويسعدني تلقي أسئلتكم وأفكاركم في أي وقت.