بناء خوادم MCP
أساسيات خادم MCP
انتهينا من الهندسة، وهذا هو الجزء الذي ستكتبه بنفسك.
دورة حياة الخادم
لأن البروتوكول بلا حالة، فدورة حياة الخادم هي في معظمها دورة حياة عملية لا دورة حياة بروتوكول. ومعرفة المرحلة التي علقت فيها تمثل معظم عملية التصحيح، والمرحلتان الأوليان تبدوان متطابقتين من الخارج.
دورة حياة الخادم — أين تحدث الأعطال
تُنفَّذ الاستيرادات وتُسجَّل المعالجات. خطأ استيراد واحد يقتل الخادم هنا فلا يعرض المضيف شيئاً إطلاقاً
يتصل العميل عبر stdio أو Streamable HTTP. المسار الخطأ أو المفسّر الخطأ يوقفك هنا
كل طلب يصل مكتفياً بذاته، يُتحقق منه عبر _meta الخاص به، ثم يُوزَّع. ويتكرر ذلك باستقلال إلى ما لا نهاية
حرّر مقابض الملفات والاتصالات. فقد يقطع العميل النقل في أي لحظة دون إنذار
لاحظ ما ليس مرحلة هنا: لا توجد خطوة لتبادل القدرات. فالقدرات تصل مع كل طلب بدل الاتفاق عليها مرة واحدة في البداية، ولهذا لا يحمل الاتصال أي ذاكرة، وعلى معالجك أن يكون قادراً على خدمة طلب بارد في أي لحظة.
إنشاء نسخة الخادم
from mcp.server import MCPServer
# إنشاء خادم باسم فريد
mcp = MCPServer(name="my-awesome-server")
الصنف MCPServer هو الواجهة عالية المستوى، وهو ما يستخدمه دليل البدء السريع الخاص بالـ SDK نفسه.
يوجد تحته أيضاً صنف Server منخفض المستوى تلجأ إليه حين تحتاج إلى تشكيل استجابات البروتوكول
الخام بنفسك، وهو ما يفعله درس معالجة الأخطاء بالضبط. أما في بقية هذه
الدورة فـ MCPServer هو الخيار الافتراضي الصحيح.
اسم الخادم يظهر في السجلات ويساعد في تحديد خادمك عند تكوين عدة خوادم MCP.
تسجيل الأدوات
الأدوات هي الطريقة الأساسية للتفاعل بين الذكاء الاصطناعي وخادمك. كل أداة تحتاج:
| المكون | من أين يأتي |
|---|---|
| name | اسم الدالة، ما لم تتجاوزه صراحة |
| description | نص التوثيق — وهذا هو الجزء الذي يقرأه النموذج |
| inputSchema | يُولَّد من تلميحات الأنواع والقيم الافتراضية |
from typing import Literal
@mcp.tool()
def get_weather(city: str, units: Literal["celsius", "fahrenheit"] = "celsius") -> str:
"""الحصول على الطقس الحالي لمدينة، مثل "لندن" أو "طوكيو"."""
return fetch_weather(city, units)
النوع Literal هنا يؤدي عملاً حقيقياً: فهو يتحول إلى enum داخل المخطط المُولَّد، وبذلك
يُقيَّد النموذج بالقيمتين اللتين يعالجهما كودك فعلاً بدلاً من أن نثق بأنه سيخمّنهما.
كل ما يمكنك التعبير عنه بتلميح نوع هو شيء تتوقف عن التحقق منه يدوياً.
التعامل مع استدعاءات الأدوات
الدالة نفسها هي المعالِج. تصل الوسائط كمعاملات عادية، بعد التحقق منها مسبقاً وفق المخطط الذي أنتجته تلميحات الأنواع:
@mcp.tool()
async def get_weather(city: str, units: Literal["celsius", "fahrenheit"] = "celsius") -> str:
"""الحصول على الطقس الحالي لمدينة، مثل "لندن" أو "طوكيو"."""
weather = await fetch_weather(city, units)
return f"الطقس في {city}: {weather}"
لا يوجد جدول توزيع ولا سلسلة if name == ...، لأن الـ SDK يوجّه الاستدعاء إلى الدالة التي
زيّنتها. يمكن أن تكون المعالِجات def أو async def؛ استخدم async def فور لمسك للشبكة،
وهو ما يحدث فوراً في معظم الأدوات الحقيقية.
أوصاف الأدوات مهمة
الوصف ليس توثيقاً. إنه الشيء الوحيد الذي يقرأه النموذج ليقرر ما إذا كانت هذه الأداة هي المناسبة لما طلبه المستخدم للتو — وهو يقرأ قائمة مرشحين على البارد، قبل أن يعرف شيئاً عن موضوع المحادثة.
الأداة نفسها موصوفة بطريقتين
ثلاثة تغييرات، كل واحد منها يعالج إخفاقاً بعينه:
| التغيير | الإخفاق الذي يمنعه |
|---|---|
كتابة knowledge_base كاملة بدل kb | تخمين النموذج لما يشمله الاختصار |
| تسمية ما بداخلها: السياسات وأدلة التشغيل والتقارير | تجاهل الأداة في أسئلة كانت تستطيع الإجابة عنها |
| تسمية ما ليس بداخلها: لا كود ولا تذاكر | استدعاء الأداة في أسئلة ستفشل فيها |
السطر الأخير هو ما يغفله الناس عادة. إخبار النموذج بحدود الأداة لا يقل قيمة عن إخباره ببدايتها.
في القسم التالي، سنضيف موارد لكشف البيانات التي يمكن للذكاء الاصطناعي قراءتها. :::
سجّل الدخول للتقييم