SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-28

تغییرات سرورهای Stateless در پروتکل MCP

در بازبینی 2026-07-28 پروتکل MCP، نشست‌ها و handshake حذف شدند. بررسی کنید که این تغییرات چگونه بر تنظیمات reverse proxy، مکانیزم health checks و احراز هویت تأثیر می‌گذارد.

سرور MCP بدون وضعیت (stateless) چیست

یک سرور MCP بدون وضعیت، هیچ‌گونه وضعیت (state) مختص به کلاینت را بین درخواست‌ها نگهداری نمی‌کند. هر درخواست شامل نسخه پروتکل، قابلیت‌های کلاینت و اعتبارنامه‌هایی است که سرور برای پاسخ‌دهی به آن نیاز دارد؛ بنابراین هر پردازشی روی هر ماشینی می‌تواند به هر درخواستی پاسخ دهد. پروتکل MCP (مخفف Model Context Protocol، فرمت ارتباطی که ایجنت‌ها برای دسترسی به ابزارها استفاده می‌کنند) این موضوع را در بازبینی 2026-07-28 به یک قانون تبدیل کرد که طی آن، handshake مربوط به initialize و نشست HTTP زیرین آن حذف شد. تمام مطالب اینجا مربوط به سمت سرور این پروتکل است؛ بنابراین اگر سمت ایجنت برای شما جدید است، یک مسیر مرحله‌بندی‌شده برای یادگیری ایجنت‌های هوش مصنوعی چرخه‌ای را پوشش می‌دهد که پیش از اهمیت یافتن جزئیات HTTP، تصمیم به فراخوانی یک ابزار می‌گیرد.

این تمام نکته عملیاتی ماجراست. سروری که هیچ‌چیز را به ازای هر کلاینت نگه نمی‌دارد، می‌تواند پشت یک load balancer معمولی بدون نیاز به session affinity قرار بگیرد، در حین deploy بدون قطع ارتباط کلاینت‌ها restart شود و به‌جای یک پردازش، به صورت چهار پردازش کاملاً یکسان اجرا گردد. یک سرور مبتنی بر نشست (session-oriented) بدون تجهیزات اضافی، هیچ‌کدام از این کارها را انجام نمی‌دهد.

پروتکل Model Context Protocol یک پروتکل بدون وضعیت است: تمام اطلاعات مورد نیاز برای پردازش یک درخواست، درون خودِ آن درخواست گنجانده شده است. سرور هر درخواست را به‌طور مستقل پردازش می‌کند؛ هیچ وضعیتی نباید از درخواست‌های قبلی، حتی آن‌هایی که روی همان اتصال یا stream بوده‌اند، استنباط شود.

Stateless به این معنا نیست که سرور هیچ داده‌ای ذخیره نمی‌کند. پایگاه داده، queue و cache شما همچنان وجود دارند. منظور این است که protocol روی connection هیچ stateای حمل نمی‌کند؛ بنابراین سرور نباید connection، process یا socket باز را جایگزین «این client در میانهٔ یک مکالمه» در نظر بگیرد. این تفکیک را می‌توان در برنامه‌ای که داده‌های خود را از قبل در اختیار دارد، به‌سادگی مشاهده کرد: MCP server فقط‌خواندنی openGym به پرسش‌های مربوط به سابقهٔ تمرین پاسخ می‌دهد؛ این سابقه در پایگاه دادهٔ خود برنامه قرار دارد و هیچ‌یک از بخش‌های این storage به connectionای که هر request از طریق آن دریافت شده است، وابسته نیست.

تغییرات نسخه 2026-07-28

2026-07-28 نسخه فعلی مشخصات تا اوت 2026 است. در مقایسه با 2025-11-25، این نسخه پنج موردی را که برای پشتیبانی از نشست‌ها (sessions) وجود داشت، حذف کرده است:

  • درخواست initialize و اعلان notifications/initialized. دیگر هیچ‌گونه دست‌دادن (handshake) وجود ندارد (SEP-2575).
  • هدر Mcp-Session-Id و خاتمه نشست با استفاده از HTTP DELETE (SEP-2567).
  • استریم مستقل HTTP GET که سرورها اعلان‌ها را روی آن ارسال می‌کردند. این مورد با subscriptions/listen جایگزین شده است؛ یک POST معمولی که پاسخ آن یک استریم با عمر طولانی است.
  • قابلیت ازسرگیری استریم SSE (رویدادهای ارسالی از سمت سرور). هدر Last-Event-ID و شناسه‌های هر رویداد حذف شده‌اند؛ بنابراین در صورت قطع شدن استریم، درخواست در حال اجرا از دست می‌رود و کلاینت باید آن را به عنوان یک درخواست جدید با شناسه درخواست جدید ارسال کند.
  • ping، logging/setLevel و notifications/roots/list_changed. سطح لاگ اکنون یک فیلد در سطح هر درخواست است، io.modelcontextprotocol/logLevel در _meta.

یک متد اضافه شده است که تمام سرورها باید آن را پیاده‌سازی کنند. server/discover نسخه‌های پروتکل پشتیبانی‌شده، قابلیت‌ها و هویت سرور را در یک فراخوانی بازمی‌گرداند. این متد نزدیک‌ترین مورد باقی‌مانده به یک دست‌دادن (handshake) است و فراخوانی آن برای کلاینت‌ها اختیاری است.

چرا اجرای session transport در محیط عملیاتی دشوار بود

در نسخه 2025-11-25 و نسخه‌های پیش از آن، سرور می‌توانست در زمان مقداردهی اولیه، یک session ID تولید کرده و آن را در هدر Mcp-Session-Id در پاسخ InitializeResult بازگرداند. سپس کلاینت موظف بود آن هدر را در تمامی درخواست‌های بعدی ارسال کند. نسخه پروتکل توافق‌شده و قابلیت‌های کلاینت در حافظه سرور ذخیره می‌شد و با آن ID فراخوانی می‌شد. هر یک از این انتخاب‌ها هزینه عملیاتی خاص خود را داشت.

  • راه‌اندازی مجدد سرور (restart)، جدول نشست‌ها (session table) را پاک می‌کرد. طبق مشخصات فنی، سرور باید به هر درخواستی که حاوی یک session ID نامعتبر بود با 404 Not Found پاسخ می‌داد و کلاینت نیز ملزم بود فرآیند را با یک InitializeRequest جدید از نو آغاز کند. در نتیجه، هر بار استقرار (deploy) به معنای قطع و وصل مجدد برای تمامی کلاینت‌های متصل بود.
  • نسخه دوم (replica) از نشست‌های نسخه اول بی‌اطلاع بود. مقیاس‌پذیری (scaling out) مستلزم استفاده از sticky routing در load balancer یا یک حافظه اشتراکی برای نشست‌ها بود که هر replica باید در هر درخواست آن را می‌خواند.
  • جدول نشست‌ها فضایی از حافظه را اشغال می‌کرد که با افزایش تعداد کلاینت‌های غیرفعال (idle)، رشد می‌یافت. ارسال DELETE اختیاری بود و کلاینت‌هایی که بدون ارسال آن قطع می‌شدند، ورودی‌های بلااستفاده‌ای را در حافظه باقی می‌گذاشتند.
  • نتایج لیست‌ها می‌توانست در هر اتصال متفاوت باشد، بنابراین استفاده از کش (caching) در مقابل سرور ناامن بود.

حذف نشست‌ها هر چهار مشکل فوق را به‌طور همزمان برطرف می‌کند. این تغییری است که پیش از تغییر هرگونه پیکربندی، باید آن را درک کنید.

محتوای هر درخواست در حال حاضر

هر درخواست POST به endpoint مربوط به MCP به‌صورت مستقل عمل می‌کند. نسخهٔ پروتکل و قابلیت‌های کلاینت در بدنهٔ درخواست تحت _meta ارسال می‌شوند و فیلدهای منتخب برای اینکه واسط‌ها (intermediaries) بتوانند بدون نیاز به تجزیه JSON درخواست را مسیریابی کنند، در هدرهای HTTP نیز منعکس می‌شوند.

POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather
Authorization: Bearer <access token>

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {"location": "Seattle, WA"},
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {"name": "ExampleClient", "version": "1.0.0"},
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

وجود io.modelcontextprotocol/protocolVersion و io.modelcontextprotocol/clientCapabilities در هر درخواست الزامی است. clientInfo الزامی نیست، اگرچه توصیه می‌شود کلاینت‌ها آن را ارسال کنند. درخواستی که فاقد فیلد الزامی باشد، ناقص (malformed) تلقی می‌شود و سرور باید آن را با خطای JSON-RPC به شماره -32602 و کد وضعیت HTTP 400 Bad Request رد کند.

هدر Mcp-Method در هر درخواست الزامی است. Mcp-Name در درخواست‌های tools/call، resources/read و prompts/get الزامی می‌باشد. مقدار هدر باید با بدنهٔ درخواست مطابقت داشته باشد؛ سروری که بدنه را پردازش می‌کند، در صورت عدم تطابق باید درخواست را با 400 Bad Request و کد خطای -32020، HeaderMismatch رد کند. این قانون به این دلیل وضع شده است که load balancer که بر اساس هدر مسیریابی می‌کند و سروری که بدنه را اجرا می‌کند، دو منبع حقیقت متفاوت هستند. اگر مسیریابی یا rate-limiting را بر اساس این هدرها انجام می‌دهید، ابتدا MCP-Protocol-Version را بررسی کنید: در نسخه‌های قدیمی‌تر، هدر با بدنه اعتبارسنجی نمی‌شد، بنابراین در آن نسخه‌ها مقدار هدر قابل اعتماد نیست.

عدم توافق بر سر نسخه، اکنون یک خطای عادی در هر درخواست محسوب می‌شود و دیگر به معنای شکست در handshake نیست. سروری که نسخهٔ درخواستی را پیاده‌سازی نکرده باشد، با 400 Bad Request و خطای -32022، UnsupportedProtocolVersion پاسخ می‌دهد و نسخه‌های پشتیبانی‌شدهٔ خود را در data.supported فهرست می‌کند. کلاینت یکی از موارد آن فهرست را انتخاب کرده و دوباره تلاش می‌کند.

وضعیت (State) کجا رفت: توکن‌ها، کرسرها و اشتراک‌ها

وضعیت ناپدید نشده است، بلکه به مکان‌هایی منتقل شده که می‌توانید آن‌ها را ببینید و لاگ کنید.

اعتبارنامه‌ها به هر درخواست منتقل می‌شوند. دیگر نشست (session) وجود ندارد که هویت به آن متصل شود، بنابراین توکن دسترسی (access token) همراه با هر فراخوانی HTTP ارسال شده و هر بار اعتبارسنجی می‌شود. جزئیات در بخش احراز هویت در ادامه آمده است.

کرسرها (Cursors) باید موقعیت خود را حمل کنند. صفحه‌بندی در tools/list، resources/list، prompts/list و resources/templates/list از یک رشته کرسر مبهم (opaque) استفاده می‌کند و کلاینت‌ها نباید آن را تجزیه یا اصلاح کنند. در سرورهای تک‌پردازشی، مرسوم بود که offset در حافظه و با کلید نشست نگهداری شود. بدون نشست، کرسر باید برای هر replica کافی باشد تا لیست‌کردن را از سر بگیرد؛ بنابراین موقعیت را درون کرسر کدگذاری و امضا کنید، یا آن را در حافظه‌ای که بین تمام replicaها مشترک است نگه دارید. کرسر نامعتبر باید منجر به بازگشت -32602 شود. آن را امضا کنید، زیرا کرسر مبهم همچنان ورودی ارائه‌شده توسط کلاینت است که کد شما آن را رمزگشایی کرده و به آن اعتماد می‌کند.

اشتراک‌ها (Subscriptions) متعلق به یک درخواست هستند، نه یک اتصال. کلاینتی که اعلان‌های تغییر را می‌خواهد، subscriptions/listen را با فیلتری که انواع مورد نظرش را مشخص می‌کند ارسال می‌کند: toolsListChanged، promptsListChanged، resourcesListChanged و resourceSubscriptions. سرور با notifications/subscriptions/acknowledged پاسخ می‌دهد و آن جریان پاسخ را باز نگه می‌دارد. اگر جریان قطع شود، سرور چیزی را نگه نمی‌دارد و کلاینت برای بازیابی آن، subscriptions/listen را مجدداً ارسال می‌کند.

وضعیت برنامه در فراخوانی‌های متقاطع به یک هندل (handle) صریح تبدیل می‌شود. زمانی که سرور واقعاً باید چیزی را بین فراخوانی‌ها به خاطر بسپارد، پاسخ مشخصات (specification)، یک شناسه‌ است که توسط سرور تولید شده و به عنوان یک آرگومان معمولی ابزار بازگردانده می‌شود. این شناسه در طرح‌واره (schema) ابزار ظاهر می‌شود، قابل لاگ‌برداری است و هرگز توسط اتصال استنباط نمی‌شود. سروری که داده‌های واقعی هر کاربر را در پشت خود دارد، مانند یک سرور ایمیل MCP خودمیزبان، به جای نشست از این الگو استفاده می‌کند: شناسه صندوق پستی یا پیش‌نویس، یک آرگومان ابزار است، بنابراین هر replica می‌تواند فراخوانی بعدی را دریافت کند. بسیاری از ابزارها اصلاً نیازی به هندل ندارند: یک ابزار جستجو که توسط نمونه SearXNG شخصی شما پشتیبانی می‌شود، یک پرس‌وجو را دریافت کرده و نتایج را بازمی‌گرداند، بدون اینکه چیزی برای از سرگیری در فراخوانی بعدی وجود داشته باشد و بدون اینکه اهمیتی داشته باشد کدام replica پاسخ داده است.

استقرار: reverse proxy، زمان‌بندی‌ها (timeouts) و بررسی سلامت (health checks)

نقطهٔ پایانی MCP یک مسیر واحد است که درخواست‌های POST را می‌پذیرد. بخش عمدهٔ ترافیک شامل یک درخواست کوتاه و پاسخ JSON است که هر پروکسی به‌راحتی از عهدهٔ آن برمی‌آید. استثنا، پاسخ‌های streaming است که تنظیمات پیش‌فرض پروکسی در آنجا علیه شما عمل می‌کند. این همان بخشی است که هنگام انتقال از یک دموی محلی به یک سرور MCP در حال اجرا روی VPS تغییر می‌کند.

location /mcp {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_read_timeout 1h;
    proxy_send_timeout 1h;
}

proxy_buffering off اهمیت دارد، زیرا nginx به‌صورت پیش‌فرض پاسخ‌های proxied را بافر می‌کند؛ این کار باعث می‌شود رویدادهای SSE تا زمان پر شدن بافر یا پایان پاسخ، نگه داشته شوند. مشخصات فنی همچنین از سرورها می‌خواهد که در پاسخ‌های SSE از X-Accel-Buffering: no استفاده کنند و nginx به این هدر احترام می‌گذارد؛ بنابراین یک سرور صحیح، اطلاعات درست را به پروکسی شما اعلام می‌کند. با این حال، این دستورالعمل را تنظیم کنید، زیرا این بخشی است که شما کنترل آن را در دست دارید.

proxy_read_timeout به‌صورت پیش‌فرض روی 60 ثانیه تنظیم شده است. یک استریم subscriptions/listen که بیش از این مدت ساکت بماند، توسط nginx (و نه سرور شما) بسته می‌شود؛ در نتیجه لاگ‌های شما یک پردازش سالم را نشان می‌دهند، اما کلاینت با قطع استریم مواجه می‌شود. این مقدار را فقط برای location مربوط به MCP افزایش دهید، نه برای کل سرور. همچنین به سرورها توصیه می‌شود در دوره‌های سکوت، یک خط کامنت SSE (خطی که با دونقطه شروع می‌شود) به‌عنوان keep-alive ارسال کنند تا از قطع شدن استریم توسط واسط‌ها جلوگیری شود.

Caddy به تنظیمات کمتری نیاز دارد. این ابزار برای بهره‌وری شبکه به‌صورت جزئی بافر می‌کند و به‌محض اینکه پاسخ حاوی Content-Type: text/event-stream باشد، آن را تخلیه می‌کند؛ بنابراین streaming بدون نیاز به دستورالعمل‌های اضافی کار می‌کند.

mcp.example.com {
	reverse_proxy 127.0.0.1:8080 {
		health_uri /healthz
		health_interval 10s
	}
}

دقت کنید که آن بررسی سلامت (health check) به کجا اشاره دارد. بررسی فعال را با GET به سمت نقطهٔ پایانی MCP هدایت نکنید، زیرا سروری که فقط این نسخه را پیاده‌سازی کرده است، به GET و DELETE با 405 Method Not Allowed پاسخ می‌دهد و متد پیش‌فرض بررسی سلامت در Caddy برابر با GET است. در این صورت، پروکسی یک backend کاملاً سالم را به‌عنوان از دسترس خارج‌شده علامت‌گذاری می‌کند. یک مسیر ساده مانند /healthz را برای پروکسی در نظر بگیرید و پروتکل را به‌صورت جداگانه با یک درخواست POST بررسی کنید.

curl -sS https://mcp.example.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: server/discover' \
  -d '{"jsonrpc":"2.0","id":"health-1","method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'

یک 200 که حاوی لیست supportedVersions باشد، به این معنی است که پردازش بالا آمده و پروتکل را می‌فهمد. یک 404 با خطای JSON-RPC به شماره -32601 به این معنی است که پردازش فعال است اما server/discover را ارائه نمی‌دهد؛ قابلیتی که هر سرور 2026-07-28 باید آن را پیاده‌سازی کند. یک 400 با -32022 به این معنی است که بررسی‌کنندهٔ شما نسخه‌ای را درخواست کرده که این بیلد از آن پشتیبانی نمی‌کند؛ این دقیقاً همان چیزی است که باید پس از ارتقای وابستگی‌ها (dependency upgrade) شناسایی کنید. نسخهٔ متن‌باز nginx بررسی سلامت فعال ندارد، بنابراین از max_fails و fail_timeout غیرفعال روی upstream استفاده کنید و بررسی پروتکل را از طریق سیستم مانیتورینگ خود انجام دهید.

یک راه‌اندازی مجدد غلتان (rolling restart) اکنون فقط باعث از دست رفتن درخواست‌های در حال اجرا می‌شود و هیچ هزینهٔ دیگری ندارد. پردازش را تخلیه (drain) کنید، اجازه دهید POSTهای باز تمام شوند، پردازش جدید را شروع کنید و کلاینت‌ها هر آنچه شکست خورده را دوباره ارسال می‌کنند. تنها چیزی که همچنان از دست می‌دهید، هر استریم subscriptions/listen باز است، زیرا آن استریم یک اتصال زنده به یک پردازش خاص است. Stateless بودن، session affinity را حذف کرد، اما connection affinity را برای استریمی که همین لحظه باز است حذف نکرد و هیچ قانون مسیریابی نمی‌تواند آن را اصلاح کند. کلاینت می‌تواند تفاوت را تشخیص دهد: استریمی که با نتیجهٔ خالی subscriptions/listen پایان می‌یابد، به‌درستی بسته شده است و استریمی که بدون آن پایان می‌یابد، قطع شده تلقی می‌شود که کلاینت ممکن است آن را دلیلی برای اتصال مجدد بداند.

Caching برای اولین بار ممکن می‌شود. نتایج متدهای لیست اکنون دارای ttlMs و cacheScope هستند و cacheScope: "public" به واسط‌های اشتراکی اعلام می‌کند که می‌توانند پاسخ را کش کنند. این کار تنها به این دلیل ایمن است که نتایج لیست دیگر بر اساس هر اتصال تغییر نمی‌کنند، که نتیجهٔ مستقیم حذف sessionها است.

چرا با نبود نشست (session)، احراز هویت تغییر می‌کند

در حالت استفاده از نشست، وسوسه‌انگیز بود که یک‌بار در initialize احراز هویت انجام شود و سپس شناسه نشست (session ID) به عنوان مدرک برای تمام درخواست‌های بعدی در نظر گرفته شود. شناسه نشستی که به این شکل استفاده شود، یک اعتبارنامه حامل (bearer credential) بدون مخاطب (audience)، بدون تاریخ انقضا و بدون مسیر ابطال است که توسط سرور خودتان صادر شده است. حذف نشست‌ها این میان‌بر را از بین می‌برد و جایگزین آن سخت‌گیرانه‌تر است.

یک سرور MCP محافظت‌شده، به عنوان یک سرور منبع (resource server) در OAuth 2.1 عمل می‌کند. هر درخواست HTTP از سمت کلاینت باید حاوی Authorization: Bearer <access token> باشد و سرور در هر درخواست، توکن را اعتبارسنجی می‌کند. اعتبارسنجی شامل بررسی مخاطب (audience) است: سرور باید طبق RFC 8707 (شاخص‌های منبع برای OAuth 2.0) تأیید کند که توکن مشخصاً برای همان سرور صادر شده است و نباید توکن‌های مربوط به مقاصد دیگر را بپذیرد یا منتقل کند. کلاینت‌ها با ارسال پارامتر resource به همراه URI استاندارد سرور، مخاطب صحیح را درخواست می‌کنند.

کشف (Discovery) بر اساس یک چالش انجام می‌شود. هنگامی که درخواستی بدون توکن قابل‌استفاده برسد، سرور با 401 Unauthorized پاسخ می‌دهد.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
                         scope="files:read"

کلاینت resource_metadata را می‌خواند، آن سند را دریافت می‌کند (RFC 9728، فراداده منبع محافظت‌شده OAuth 2.0 که سرورهای MCP ملزم به پیاده‌سازی آن هستند)، سرور احراز هویت را پیدا کرده و جریان (flow) را اجرا می‌کند. یک توکن معتبر که دسترسی‌های کافی ندارد، با 403 Forbidden و به همراه error="insufficient_scope" و دامنه‌های (scopes) مورد نیاز برای آن عملیات، مواجه می‌شود.

این موضوع دو پیامد برای نحوه اجرای شما دارد. اعتبارسنجی توکن اکنون به جای یک‌بار در هر نشست، در هر درخواست انجام می‌شود؛ بنابراین یک رفت‌وبرگشت شبکه به نقطه پایانی introspection در هر فراخوانی، در تأخیر (latency) شما نمایان خواهد شد: توکن‌هایی را ترجیح دهید که بتوانید به‌صورت محلی با بررسی امضا، مخاطب و تاریخ انقضا تأیید کنید، یا نتیجه اعتبارسنجی را برای یک بازه زمانی کوتاه با کلید توکن کش (cache) کنید. و از آنجا که نشستی برای نگهداری هویت وجود ندارد، مجوزها باید در هر فراخوانی از روی توکن محاسبه شوند. این روش نسبت به مدل نشست صادقانه‌تر است و با رویه گسترده‌ترِ دور نگه داشتن اعتبارنامه‌ها از فرآیند عامل (agent process) همخوانی دارد که در دور نگه داشتن اسرار از یک عامل هوش مصنوعی پوشش داده شده است. دامنه‌ها (scopes) فقط محدود می‌کنند که یک توکن پس از رسیدن درخواست به شما چه کاری می‌تواند انجام دهد؛ روی ماشینی که عامل روی آن اجرا می‌شود، استفاده از افزونه‌هایی که قوانین مجوز ابزار و سقف بودجه را اضافه می‌کنند تعیین می‌کند که اصلاً کدام فراخوانی‌ها انجام شوند.

چه چیزی در مورد این بازبینی درست است و چه چیزی نیست

همه موارد فوق، بازبینی 2026-07-28 را توصیف می‌کنند. این متن توصیف‌کننده همیشگی MCP نیست و سروری را که سال گذشته مستقر کرده‌اید، توصیف نمی‌کند.

کلاینت‌ها و سرورها در نسخه 2025-11-25 و قبل از آن، همچنان از مدل handshake استفاده می‌کنند. این مشخصات، آن بازبینی‌ها را قدیمی (legacy) و بازبینی‌های مبتنی بر متادیتای هر درخواست را مدرن می‌نامد. سروری که فقط از این بازبینی پشتیبانی می‌کند، در مواجهه با یک کلاینت قدیمی باید در endpoint مربوط به MCP، پاسخ 405 Method Not Allowed را به GET یا DELETE بدهد، هرگونه هدر Mcp-Session-Id را بدون ایجاد یا بازتاب آن نادیده بگیرد و Last-Event-ID را نیز نادیده بگیرد، زیرا استریم‌ها در این مدل قابل ازسرگیری (resumable) نیستند. یک سرور دو-دوره‌ای ممکن است هر دو مدل را در یک endpoint ارائه دهد: درخواستی که دارای _meta مدرن باشد به صورت stateless (بدون وضعیت) پاسخ داده می‌شود و درخواست initialize، معناشناسی نشست (session) قدیمی‌تر را انتخاب می‌کند.

بنابراین پیش از اعتماد به هر یک از این موارد، رشته بازبینی (revision string) را بررسی کنید. اگر SDK شما همچنان initialize ارسال می‌کند، نشست‌ها برای استقرار شما همچنان واقعی هستند و مشکلات مربوط به ساختار نشست که در بالا ذکر شد، همچنان بر عهده شماست. همین موضوع در سمت کلاینت نیز صدق می‌کند: یک پردازش عامل (agent process) روی سیستم خودتان، مانند تنظیمات موجود در اجرای یک عامل کدنویسی روی VPS، تنها در صورتی از نظر این مفهوم stateless است که کتابخانه مورد استفاده آن، از یک بازبینی مدرن پشتیبانی کند. نسخه‌ای را که runtime شما مذاکره می‌کند بخوانید، سپس بازبینی منطبق در مشخصات را مطالعه کنید و این صفحه را به عنوان توصیفی از یک بازبینی نام‌گذاری‌شده در نظر بگیرید، نه توصیف کلی پروتکل.

FAQ

آیا stateless بودن یک MCP server به این معناست که نمی‌توانم چیزی ذخیره کنم؟

خیر. واژه stateless به پروتکل اشاره دارد، نه به برنامه شما. پایگاه‌های داده، صف‌ها و کش‌ها دقیقاً مانند قبل کار می‌کنند. آنچه تغییر کرده این است که وضعیت (state) مربوط به چندین فراخوانی باید توسط یک شناسه صریح که کلاینت در هر درخواست ارسال می‌کند، ارجاع داده شود؛ مانند یک handle که توسط سرور در آرگومان ابزار (tool argument) تولید شده است. کاری که نباید انجام دهید، استنباط context از روی اتصال است: مشخصات فنی می‌گوید سرور نباید برای تعیین قابلیت‌ها، نسخه پروتکل یا هویت کلاینت به درخواست‌های قبلی در همان اتصال تکیه کند، زیرا هر درخواست این موارد را در _meta ارائه می‌دهد.

آیا هنوز به sticky sessions در load balancer خود نیاز دارم؟

برای درخواست‌های معمولی، خیر. تحت نسخه 2026-07-28، هر POST نسخه پروتکل، قابلیت‌ها و اعتبارنامه‌های خاص خود را حمل می‌کند، بنابراین هر replica می‌تواند به هر درخواستی پاسخ دهد و استفاده از round-robin مشکلی ندارد. تنها مورد طولانی‌مدت باقی‌مانده، جریان پاسخ subscriptions/listen است که یک اتصال باز واحد به یک پردازش واحد است. این جریان با پایان یافتن آن پردازش خاتمه می‌یابد و کلاینت برای برقراری مجدد آن، subscriptions/listen را دوباره ارسال می‌کند. این موضوع مربوط به طول عمر اتصال است نه session affinity، و هیچ قانون مسیریابی نمی‌تواند مانع آن شود.

چه اتفاقی برای Mcp-Session-Id و جریان HTTP GET افتاد؟

هر دو در نسخه 2026-07-28 و تحت SEP-2567 و SEP-2575 حذف شدند. سروری که فقط این نسخه را پیاده‌سازی می‌کند باید به 405 Method Not Allowed تا GET و DELETE در endpoint مربوط به MCP پاسخ 405 Method Not Allowed بدهد و باید هدر Mcp-Session-Id را نادیده بگیرد، نه اینکه آن را بازگرداند. اعلان‌های تغییر که توسط سرور آغاز می‌شوند، اکنون به جای یک جریان مستقل GET، از طریق جریان پاسخ یک درخواست subscriptions/listen ارسال می‌شوند. سرورهایی که باید به کلاینت‌های قدیمی‌تر سرویس‌دهی کنند، رفتار نسخه قبلی را در کنار این نسخه پیاده‌سازی می‌کنند.

چگونه یک MCP server را بدون handshake بررسی سلامت (health check) کنم؟

از دو سطح استفاده کنید. بررسی فعال (active check) پروکسی را به یک مسیر HTTP ساده که برنامه شما ارائه می‌دهد هدایت کنید، زیرا یک GET به endpoint مربوط به MCP به درستی 405 برمی‌گرداند و باعث می‌شود یک backend سالم، به اشتباه down علامت‌گذاری شود. سپس خود پروتکل را با ارسال یک POST به server/discover بررسی کنید (که هر سرور 2026-07-28 باید آن را پیاده‌سازی کند) و اطمینان حاصل کنید که پاسخ، HTTP 200 است و نسخه پروتکلی که کلاینت‌های شما استفاده می‌کنند را لیست می‌کند. یک 404 با خطای JSON-RPC به شماره -32601 به این معنی است که پردازش در حال اجراست اما آن متد را ارائه نمی‌دهد، و یک 400 با -32022 به این معنی است که نسخه‌ای که درخواست کرده‌اید توسط آن build پشتیبانی نمی‌شود.