بروتوكول MCP
درس 3 من 6
3بروتوكول MCP
الصورة الكبيرةبدون بروتوكول، كل أداة تكتبها تحتاج ربطًا خاصًّا بكل تطبيق ذكاء اصطناعي تريد استخدامها فيه. MCP (Model Context Protocol) بروتوكول مفتوح يحلّ هذا: تكتب خادم أدوات واحدًا، ويعمل مع أي عميل يدعم البروتوكول، من تطبيقات المحادثة إلى بيئات البرمجة إلى وكيلك الخاص.
ماذا تشاهد في الرسم: هذه الرسائل الثلاث هي أغلب ما يجري في جلسة: اتفاق على النسخة، ثم قائمة الأدوات التي ستوضع أمام النموذج، ثم تنفيذ get_leave_balance. كل رد يحمل نفس id الطلب، فيعرف العميل لأي سؤال هو.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
قبل المقابس الموحّدة كان لكل جهاز شاحنه الخاص. MCP مقبس موحّد للأدوات: تكتب أداتك مرة واحدة، فيستطيع أي تطبيق ذكاء اصطناعي يدعم المعيار أن يستخدمها. لا تعيد كتابتها لكل برنامج.
بروتوكول سياق النموذج MCP يعرّف خادمًا يعرض أدوات tools وموارد resources وقوالب prompts عبر رسائل JSON-RPC على stdio أو HTTP. العميل يكتشف القدرات ويستدعيها، وتختبر خادمك محليًا بأداة فحص قبل ربطه بتطبيق.
قبل أن تبدأ
🔧الأدوات كواجهة
تعرف أن الاسم والوصف والمخطّط هي ما يراه النموذج من الأداة.
📨JSON
تقرأ رسائل JSON وتكتبها، فرسائل البروتوكول كلها JSON.
الفكرة الجوهرية
بروتوكول MCP يجعل الأداة تُكتب مرة وتعمل مع كل عميل. في هذا الدرس ترى لماذا نحتاجه، ثم من يتكلّم مع من، ثم الرسائل الثلاث، ثم تكتب خادمًا وتختبره.
1لماذا بروتوكول
بدون بروتوكول، كل أداة تحتاج ربطًا خاصًّا مع كل تطبيق. عندك N أداة وM تطبيق، فتكتب N×M ربطًا.
انظر إلى الرسم: 5 أدوات و3 تطبيقات تعني 15 ربطًا بدون بروتوكول. ومع MCP تكتب 5 خوادم، و3 عملاء يفهمون البروتوكول، فالمجموع 8.
2الخادم والعميل
الخادم يعرض الأدوات وينفّذها. العميل، أي تطبيق المحادثة أو وكيلك، يكتشف الأدوات ويضعها أمام النموذج وينفّذ ما يطلبه.
انظر إلى الرسم: النموذج لا يكلّم الخادم أبدًا. الطلب يمرّ عبر العميل، والعميل هو من يطلب الموافقة قبل الأدوات الخطرة.
والخادم يعرض أكثر من أدوات. الأدوات أفعال يستدعيها النموذج، مثل create_ticket. الموارد بيانات يقرؤها العميل إلى السياق، مثل ملف أو صفحة. والقوالب جاهزة يختارها المستخدم.
3الجلسة في ثلاث رسائل
تحت الغطاء، MCP رسائل JSON-RPC بسيطة. العميل يتّفق مع الخادم، ثم يسأل عن الأدوات، ثم يطلب تنفيذ واحدة منها. نكتب الخادم بيدنا لنرى كل رسالة.
import json
TOOLS = {
"get_leave_balance": {
"description": "Return the remaining annual leave days for an employee id.",
"inputSchema": {"type": "object", "properties": {"employee_id": {"type": "string"}},
"required": ["employee_id"]},
"fn": lambda args: {"employee_id": args["employee_id"], "days_left": 12},
}
}
def handle(request):
method, rid = request["method"], request.get("id")
if method == "initialize":
result = {"protocolVersion": "2025-06-18", "capabilities": {"tools": {}},
"serverInfo": {"name": "hr-server", "version": "0.1"}}
elif method == "tools/list":
result = {"tools": [{"name": n, "description": t["description"], "inputSchema": t["inputSchema"]}
for n, t in TOOLS.items()]}
elif method == "tools/call":
tool = TOOLS[request["params"]["name"]]
out = tool["fn"](request["params"]["arguments"])
result = {"content": [{"type": "text", "text": json.dumps(out)}]}
else:
return {"jsonrpc": "2.0", "id": rid, "error": {"code": -32601, "message": f"unknown method {method}"}}
return {"jsonrpc": "2.0", "id": rid, "result": result}
session = [
{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {}},
{"jsonrpc": "2.0", "id": 2, "method": "tools/list"},
{"jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": {"name": "get_leave_balance", "arguments": {"employee_id": "E-17"}}},
]
for req in session:
print("->", json.dumps(req))
print("<-", json.dumps(handle(req)))
ثلاث رسائل هي كل ما يحدث في أغلب الجلسات. initialize يتّفق فيها الطرفان على نسخة البروتوكول والقدرات. tools/list يعيد ما سيضعه العميل في سياق النموذج. وtools/call ينفّذ ويعيد النتيجة كمحتوى نصّي. هذا تبسيط تعليمي، فالمكتبة الرسمية تتولّى التفاصيل كلها، لكنه نفس الشكل الذي ستراه في أداة الفحص.
4خادم بالمكتبة الرسمية
في العمل الحقيقي لا تكتب البروتوكول بيدك. المكتبة الرسمية تتولّى الرسائل والأخطاء والنقل، وأنت تكتب دالة عادية.
نفس أداة رصيد الإجازات، لكن بمكتبة Python الرسمية. يحتاج pip install mcp، فهو للقراءة هنا.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hr-server")
BALANCES = {"E-17": 12, "E-42": 3}
@mcp.tool()
def get_leave_balance(employee_id: str) -> dict:
"""Return the remaining annual leave days for one employee.
Use it when someone asks how many leave days they have left.
employee_id looks like 'E-17'.
"""
if employee_id not in BALANCES:
return {"error": f"No employee {employee_id}. Ask for the id on their badge."}
return {"employee_id": employee_id, "days_left": BALANCES[employee_id]}
if __name__ == "__main__":
mcp.run() # stdio by default
المكتبة تقرأ اسم الدالة كاسم للأداة، وتعليق التوثيق كوصف، وتلميحات الأنواع كمخطّط. أي أن كل ما تعلّمته في درس الأدوات يُكتب هنا في مكانه الطبيعي: اسم واضح، ووصف يقول متى، وخطأ يقول ماذا بعد.
5الاختبار المحلي ثم الربط
قبل أن يلمس أي نموذج خادمك، افتحه في أداة الفحص الرسمية (MCP Inspector). هي تتّصل بخادمك، وتعرض أدواته، وتنفّذها بيدك.
# 1. open the inspector against your server, then list and call tools by hand
npx @modelcontextprotocol/inspector python hr_server.py
# 2. once it works, register it in a client's MCP config (the file name differs per client)
{
"mcpServers": {
"hr": { "command": "python", "args": ["/full/path/to/hr_server.py"] }
}
}
في أداة الفحص جرّب كل أداة بمدخلات صحيحة وخاطئة وفارغة، واقرأ رسائل الخطأ كأنك النموذج. إن لم تفهم منها ماذا تفعل بعد ذلك، فالنموذج لن يفهم أيضًا.
كيف يعمل، خطوة بخطوة: كيف يجد العميل أدوات خادم MCP ويشغّلها
العميل يشغّل الخادم أو يتّصل به، ويرسل initialize ليتّفقا على النسخة والقدرات.
يرسل tools/list فيعود الخادم بأسماء الأدوات وأوصافها ومخطّطاتها.
يضع العميل هذه القائمة في سياق النموذج، والنموذج يطلب أداة بمعاملات.
يرسل العميل tools/call بعد الموافقة إن لزمت، ويعود الخادم بالنتيجة نصًّا يُلحق بالمحادثة.
أعمق قليلًا
خادم MCP كود يعمل بصلاحياتك. أداة run_command أو delete_file في خادم تثبّته تعني أن أي نموذج متّصل يستطيع طلب تشغيلها. ثبّت فقط خوادم تثق بمصدرها وقرأت أدواتها، والعملاء الجيدون يطلبون موافقتك قبل تنفيذ الأدوات الخطرة، وهو موضوع درس الحقن والصلاحيات.
مثال محلول
تحت الغطاء، MCP رسائل JSON-RPC بسيطة بين عميل وخادم. العميل يسأل «ما أدواتك؟» والخادم يعيد قائمة بالأسماء والأوصاف والمخطّطات، ثم العميل يطلب «شغّل هذه الأداة بهذه المعاملات». لنكتب خادمًا صغيرًا بيدنا يردّ على هذه الرسائل.
print في خادم يعمل عبر stdio. الإخراج القياسي هو قناة البروتوكول نفسها، فأي سطر إضافي يكسر الرسائل ويقطع الاتصال بلا رسالة واضحة. اكتب السجلات إلى stderr أو ملف.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| MCP نموذج أو مكتبة ذكاء اصطناعي. | هو بروتوكول مفتوح لرسائل JSON-RPC بين عميل وخادم. لا يحوي نموذجًا، بل يربط الأدوات بأي عميل يتحدّثه. |
| خادم MCP من مصدر عام آمن لأنه يتبع البروتوكول. | الخادم كود يعمل بصلاحياتك. ثبّت فقط ما تثق بمصدره وقرأت أدواته. |
| لا بد أن يعمل الخادم عبر الشبكة. | مع stdio يشغّله العميل عملية فرعية على نفس الجهاز، وهذا أبسط بداية للأدوات المحلية. |
جرّب بنفسك
كتبت أداة لعميل محادثة واحد بربط خاص. لماذا تعيد كتابتها كخادم MCP؟
هذا كل هدف البروتوكول: الأداة تُكتب مرة. حين تنتقل لعميل آخر أو تبني وكيلك، تبقى الأداة كما هي.
خادمك عبر stdio يفشل في الاتصال بلا رسالة واضحة. ما أول شيء تتحقّق منه؟
في stdio الإخراج القياسي محجوز للبروتوكول. print واحدة للتصحيح تكفي لكسر الاتصال.
بطاقات مراجعة سريعة
create_ticket). الموارد بيانات يقرؤها العميل ويضعها في السياق (ملف، صفحة). والقوالب (Prompts) قوالب جاهزة يختارها المستخدم.تحقق من فهمك
ما الفرق بين أداة ومورد في MCP؟ أعطِ مثالًا لكل منهما في نظام موارد بشرية.
الأداة فعل يقرّر النموذج استدعاءه بمعاملات، مثل submit_leave_request(start, end). المورد بيانات يقرؤها العميل ويضعها في السياق، غالبًا باختيار المستخدم، مثل «دليل سياسات الموظفين» كملف. الأداة قد تغيّر شيئًا، والمورد للقراءة.
ما الذي يضعه العميل في سياق النموذج من رد tools/list، ولماذا يهمّك ذلك وأنت تكتب الخادم؟
الأسماء والأوصاف ومخطّطات المعاملات. فالـdocstring وتلميحات الأنواع في دالتك هي ما يختار به النموذج، وتستحق نفس عناية درس الأدوات.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.