في 28 يوليو 2026 نشر Model Context Protocol مراجعة جديدة لمواصفته. يبدأ سجل التغييرات ببندين يغيّران طريقة عمل كل خادم بعيد: أُلغيت الجلسات على مستوى البروتوكول، وأُلغيت معها مصافحة initialize. صار كل طلب يحمل الآن كل ما يحتاجه الخادم للرد عليه.
بالنسبة لكثير من الخوادم يعني هذا عملية ترحيل. أما بالنسبة لخادم يقرأ وثائق الهوية فهو أقرب إلى تأكيد، لأن هذا النوع من الخوادم كانت لديه أسباب وجيهة ليكون بلا حالة من الأصل. يمر هذا المقال على المراجعة من وجهة نظر خادم أدوات: أي التغييرات يمكنه تجاهلها، وأيها يجب أن يبنيها، وأيها يستحق اعتماده مبكرًا. الأمثلة مأخوذة من خادم نشغّله بأنفسنا، لذا فالنواقص المذكورة أدناه نواقصنا نحن أيضًا.
هذه مدونة doc.cheap، وهي واجهة OCR لجوازات السفر وبطاقات الهوية يقف أمامها خادم MCP. اقرأ الأجزاء المتعلقة بالمنتج مع أخذ ذلك في الحسبان.
الخلاصة
| التغيير في 2026-07-28 | ما يفعله خادم أدوات بلا حالة |
|---|---|
لا Mcp-Session-Id ولا جلسات |
لا شيء، إن لم يحتفظ بجلسة قط |
لا مصافحة initialize؛ الإصدار والقدرات في _meta لكل طلب |
قراءتها مع كل طلب ومطابقتها مع ترويسة MCP-Protocol-Version |
server/discover |
تنفيذه: إنه MUST |
ترويستا Mcp-Method وMcp-Name إلزاميتان |
التحقق من تطابقهما مع جسم الطلب |
resultType في كل نتيجة |
إرسال "complete" |
ttlMs وcacheScope في نتائج القوائم والقراءة |
اختيار مدة للتخزين المؤقت ونطاق |
| الأدوات بترتيب حتمي | إبقاء ترتيب التسجيل ثابتًا |
| أُزيل تدفق HTTP GET وإمكانية الاستئناف | الرد على GET وDELETE بـ 405 |
| Roots وSampling وLogging صارت مهملة | استقبال الملفات كوسائط للأدوات، والتسجيل إلى stderr أو OpenTelemetry |
يشرح باقي المقال كل سطر، ولماذا يهم أكثر لخادم يتعامل مع الوثائق.
انتهت الجلسات، وهذا يناسب خوادم الوثائق
يزيل البند الأول في سجل التغييرات "protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport"، أي الجلسات على مستوى البروتوكول وهذه الترويسة من ناقل Streamable HTTP. ونقاط القوائم مثل tools/list "no longer vary per-connection"، أي لم تعد تختلف من اتصال إلى آخر، والخوادم التي تحتاج إلى حالة بين الاستدعاءات "use explicit, server-minted handles passed as ordinary tool arguments": تستخدم مقابض صريحة يصدرها الخادم وتُمرَّر كوسائط عادية للأدوات.
نادرًا ما يحتاج خادم الوثائق إلى جلسة أصلًا. فكّر فيما قد تحمله الجلسة: مفتاح API، وربما صورة تنتظر القراءة، وربما آخر نتيجة. كل ذلك بيانات تفضّل ألا تبقيها في ذاكرة عملية بين الاستدعاءات، حيث قد يسلّمها خطأ برمجي إلى المستدعي التالي.
خادمنا المستضاف على https://mcp.doc.cheap/mcp بُني بهذه الطريقة قبل هذه المراجعة. يقول الكود إنه "stateless on purpose"، أي بلا حالة عن قصد: كل طلب POST يحصل على خادم جديد وناقل جديد، يرد ثم يُتخلَّص منه، وبذلك "no session lives in memory, a restart loses nothing, and one caller's key can never be seen by another caller's request": لا تعيش أي جلسة في الذاكرة، ولا يُفقد شيء عند إعادة التشغيل، ولا يمكن لطلب مستدعٍ أن يرى مفتاح مستدعٍ آخر. يُنشأ الناقل دون مولّد لمعرّفات الجلسات، وهذا ما يجعله بلا حالة في TypeScript SDK، ويرد بـ JSON عادي بدلًا من تدفق.
ونمط المقابض الذي تصفه المراجعة موجود أيضًا. عندما يريد agent استعادة نتيجة محفوظة، لا يعتمد على جلسة لتتذكرها. تعيد scan_document معرّف فحص، وتأخذ الأداتان get_scan وdelete_scan هذا المعرّف كوسيط scan_id عادي. المعرّف هو المقبض. يستطيع أي مثيل من الخادم الرد عليه، وكذلك عميل يعيد الاتصال غدًا.
إن كان خادمك يحتفظ بشيء لكل جلسة، فالسؤال هو نفسه: هل يمكن أن يتحول إلى معرّف يعيده الخادم ويرسله العميل مرة أخرى؟ في العمل مع الوثائق يكون الجواب غالبًا نعم، وغالبًا ما يكون مكسبًا للخصوصية أيضًا.
لا مصافحة: الإصدار يسافر مع كل طلب
البند الثاني يجعل MCP بلا حالة على مستوى البروتوكول. أُزيل تبادل initialize وnotifications/initialized، و"every request now carries its protocol version and client capabilities in _meta": صار كل طلب يحمل إصدار البروتوكول وقدرات العميل في _meta. وعلى HTTP يُرسل الإصدار أيضًا في ترويسة. تقول صفحة Streamable HTTP إن كل طلب POST "MUST include an MCP-Protocol-Version header"، وإن قيمتها يجب أن تطابق القيمة في _meta؛ وإلا يرد الخادم بـ 400 مع خطأ HeaderMismatch.
هكذا يبدو استدعاء أداة وثائق بالشكل الجديد. الترويسات ومفاتيح _meta تتبع مثال المواصفة نفسها؛ أما الأداة ووسائطها فهي من عندنا، مع عناصر نائبة:
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: scan_document
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "scan_document",
"arguments": { "image_base64": "<base64 image>", "retain_hours": 0 },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "<client>", "version": "<version>" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
بالنسبة للخادم، يعني هذا ثلاثة فحوص في كل طلب بدلًا من مصافحة واحدة:
- الإصدار. إن لم يدعم الخادم الإصدار المطلوب، يرد بـ 400 مع
UnsupportedProtocolVersionErrorيسرد الإصدارات التي يدعمها. - الترويسات مقابل جسم الطلب.
Mcp-Methodإلزامية في كل طلب، وMcp-Nameفيtools/callوresources/readوprompts/get. إنهما تعكسان الطريقة واسم الأداة، وأي عدم تطابق هو خطأHeaderMismatch. - القدرات. كل ما كان الخادم يتذكره من
initializeيقرؤه الآن من الطلب الذي بين يديه.
الترويسات أهم مما تبدو لخادم وثائق. استدعاء scan_document يحمل صورة كاملة بترميز base64؛ وخادمنا المستضاف يقبل أجسام طلبات حتى 36 MiB. مع وجود Mcp-Method وMcp-Name في الترويسات، تستطيع البوابة توجيه الأداة المكلفة أو عدّها أو تحديد معدلها بمعزل عن الأدوات الرخيصة، دون قراءة ذلك الجسم. وتضيف المواصفة تحذيرًا واحدًا لهذه البوابات: "SHOULD verify"، أي ينبغي أن تتحقق من أن ترويسة الإصدار تسمّي إصدارًا يتطلب التحقق من الترويسات والجسم، وأن ترفض الطلب خلاف ذلك، بدلًا من الوثوق بترويسة لم يتحقق منها أحد.
server/discover إلزامي
يضيف البند الثالث server/discover، و"servers MUST implement this RPC to advertise their supported protocol versions, capabilities, and identity": يجب على الخوادم تنفيذ هذا الـ RPC للإعلان عن إصدارات البروتوكول التي تدعمها وقدراتها وهويتها. يمكن للعملاء استدعاؤه أولًا لاختيار إصدار، أو استخدامه كفحص توافق على stdio.
هذا هو التغيير الوحيد الذي لا يستطيع خادم بلا حالة تجاوزه. ما زال خادمنا يعرض مستند اكتشاف صغيرًا على GET / (الاسم والإصدار والناقل ونقطة النهاية)، لكنه صفحة ويب عادية وليس طريقة البروتوكول. الخادم الذي يريد التحدث بإصدار 2026-07-28 يرد على server/discover بنفسه.
النتائج: resultType والتخزين المؤقت والترتيب
ثلاثة بنود أصغر تغيّر شكل النتيجة.
resultType. صارت كل نتيجة تحمله:"complete"للإجابة العادية، و"input_required"للإجابة المرحلية في طلب متعدد الجولات. يجب على العملاء "MUST treat results from earlier-protocol servers that omit the field as"complete""، أي معاملة نتائج خوادم البروتوكول الأقدم التي لا تتضمن الحقل على أنها"complete"، فيستمر الخادم الأقدم في العمل، لكن الخادم الجديد يرسله.ttlMsوcacheScope. يجب أن تحمل نتائجtools/listوprompts/listوresources/listوresources/readوresources/templates/listتلميحًا للحداثة بالمللي ثانية ونطاقًا،"public"أو"private". لخادم لا تتغير قائمة أدواته إلا عند النشر وموارده توثيق مضمَّن في الحزمة، فإنttlMsطويلًا مع"public"خيار طبيعي. أما المورد الذي يعتمد على مفتاح المستدعي فيكون"private".- ترتيب حتمي للأدوات. "SHOULD return tools from
tools/listin a deterministic order"، أي ينبغي للخوادم أن تعيد الأدوات بترتيب حتمي، لمساعدة التخزين المؤقت لدى العميل وإصابات ذاكرة الـ prompt المؤقتة. إن كانت الأدوات تُسجَّل في الكود بترتيب ثابت فأبقها كذلك؛ خادمنا يسجّل ستًا منها بالترتيب نفسه في كل مرة.
طلب المزيد من المستخدم: الطلبات متعددة الجولات
كانت المراجعات الأقدم تسمح للخادم بإرسال طلباته الخاصة إلى العميل في منتصف الاستدعاء: elicitation/create لسؤال المستخدم عن شيء، وsampling/createMessage لاستعارة نموذج العميل، وroots/list لمعرفة المجلدات المسموح بها. تستبدل المراجعة ذلك بنمط Multi Round-Trip Requests. يرد الخادم بـ resultType: "input_required" وقائمة inputRequests، ويعيد العميل الطلب الأصلي مع inputResponses.
بالنسبة لأداة وثائق، هذا هو المكان المناسب لسؤال مثل "الصورة مقصوصة من اليسار، هل ترسل أخرى؟". ولأن إعادة المحاولة هي الطلب نفسه مرة أخرى، لا يلزم الاحتفاظ بأي شيء على الخادم بين الاستدعاءين. والخادم الذي لم يستخدم أيًا من الطلبات التي يبدؤها الخادم، مثل خادمنا، ليس لديه ما يرحّله هنا.
ما صار مهملًا، وما البديل
صارت Roots وSampling وLogging مهملة. تستمر في العمل خلال فترة إهمال لا تقل عن اثني عشر شهرًا، لكن "new implementations should not add support for them": لا ينبغي للتطبيقات الجديدة إضافة دعم لها. والبدائل المقترحة تناسب خادم وثائق جيدًا:
- بدلًا من Roots، مرّر الملفات كوسائط للأدوات أو عبر الإعدادات. يقرأ خادمنا المحلي مسار ملف فقط عندما يكون مجلد صور مضبوطًا في إعداداته، وفقط داخل ذلك المجلد؛ ويُرفض أي مسار خارجه. النموذج يختار الوسيط؛ والمشغّل يرسم الحدود.
- بدلًا من Logging، سجّل إلى stderr أو OpenTelemetry. تزيل المراجعة أيضًا
logging/setLevel؛ صار مستوى السجل يُرسل مع كل طلب في_meta، ويجب ألا تُصدر الخوادم رسائل سجل لطلبات لم تطلبها. ويمكن أن يُحمل سياق التتبع (traceparent) في_metaأيضًا.
العملاء القدامى سيستمرون في الاتصال
لا ينتقل كل العملاء في اليوم نفسه، لذا تصف صفحة الناقل ما ينبغي أن يفعله خادم لا يدعم إلا 2026-07-28 مع الحركة الأقدم:
- GET أو DELETE إلى نقطة نهاية MCP: الرد 405 Method Not Allowed.
- ترويسة
Mcp-Session-Id: تجاهلها، ولا تُصدر واحدة ولا تُعدها أبدًا. - ترويسة
Last-Event-ID: تجاهلها، لأن التدفقات لم تعد قابلة للاستئناف.
خادمنا المستضاف يرد بالفعل على GET وDELETE في نقطة نهايته بـ 405 وترويسة Allow: POST, OPTIONS، ولا يُصدر أي معرّف جلسة. والعميل الذي ما زال يتحدث بمراجعة أقدم يكتشف ذلك من جسم الخطأ: تطلب منه المواصفة الرجوع إلى initialize فقط حين لا يكون جسم الـ 400 خطأً حديثًا معروفًا.
أين يقف خادمنا
لنكن دقيقين: الخادم المستضاف مبني على الإصدار 1.30.0 من TypeScript SDK، وكودنا لا يذكر 2026-07-28 في أي مكان بعد. الأجزاء المذكورة أعلاه التي تنطبق عليه بالفعل هي تلك النابعة من تصميمه: لا جلسات، والمعرّفات كمقابض، و405 لـ GET وDELETE، والملفات محصورة بالإعدادات. أما server/discover وفحوص الترويسات وresultType وحقول التخزين المؤقت فهي العمل المتبقي. تصف صفحة خادم MCP لقراءة جوازات السفر ودليل خادم MCP ما يتصل به العملاء اليوم.
إن كنت تختار واجهة وثائق للـ agents، فإن مقارنة واجهات OCR لجوازات السفر تسرد الخدمات؛ ويستحق جانب MCP أن تسأل عنه كل واحدة منها على حدة.
قائمة تحقق لخادم أدواتك
- لا تعيش أي حالة بين الطلبات؛ كل ما يحتاجه استدعاء لاحق هو معرّف يعيده الخادم.
- يُطابَق إصدار كل طلب مع ترويسة
MCP-Protocol-Version، معUnsupportedProtocolVersionErrorلأي إصدار لا تدعمه. - تُطابَق
Mcp-MethodوMcp-Nameمع جسم الطلب. - يرد
server/discoverبالإصدارات والقدرات والهوية. - تحمل النتائج
resultType؛ وتحمل نتائج القوائم والقراءةttlMsوcacheScope. - تعيد
tools/listالترتيب نفسه في كل مرة. - يحصل GET وDELETE على 405؛ وتُتجاهل ترويسات الجلسة ومعرّف الحدث.
- تصل الملفات كوسائط داخل حدود مضبوطة، لا عبر Roots.
- تذهب السجلات إلى stderr أو OpenTelemetry، لا إلى العميل.
النص الكامل على modelcontextprotocol.io، ومنشور الإصدار يقدّم الخلفية. والمزيد عن خادمنا في صفحة خادم MCP لقراءة جوازات السفر.
سؤال لك: هل كان خادم MCP الخاص بك يحتفظ في جلسة بشيء عليك الآن تحويله إلى مقبض؟ أخبرنا في التعليقات أدناه.