در نسخههای پیشین پروتکل MCP، سرورها میتوانستند در حین اجرای یک ابزار، بهصورت مستقیم از کلاینت بخواهند که مدلی زبانی را فراخوانی کند و نتیجه را برگرداند. این فرآیند که با نام Sampling شناخته میشد، سرور و کلاینت را به یک نشست وابسته میکرد و مدیریت وضعیت و هزینهها را پیچیده میساخت. در نسخهی 2026-07-28، این قابلیت به نفع اتصال مستقیم سرور به مدل منسوخ شده و ارتباط سرور به کلاینت به الگویی بیوضعیت (Stateless) به نام درخواستهای چند دوررفت (Multi Round-Trip Requests یا MRTR) تغییر کرده است.
در این رویکرد جدید، هر زمان که سرور برای تکمیل کار به داده یا تصمیم مدل در سمت کلاینت نیاز پیدا کند، پاسخی با نوع «نیاز به ورودی» (input_required) پس میفرستد و خودِ کلاینت با پاسخی تازه و شناسهای مجزا درخواست را دوباره ارسال میکند. برای حفظ امنیت، وضعیت میانی کار از طریق یک رشتهی رمزنگاریشده یا مهرومومشده به نام requestState بین دورها جابهجا میشود تا نیازی به ذخیرهسازی وضعیت در حافظهی سرور نباشد.
این درس به شما نشان میدهد چطور این جریان چند دوررفته را بدون نیاز به بستههای جانبی پیاده کنید، اعتبارسنجی وضعیت را با HMAC انجام دهید و چرخههای فراخوانی پرهزینه یا مخرب را با رعایت موازین امنیتی مهار کنید.
در پروتکل MCP نسخهی 2026-07-28، قابلیت نمونهبرداری (Sampling) برای طراحیهای جدید منسوخ شده و کانال ارسال درخواست از سرور به کلاینت حذف شده است. اگر گردش کار موجودی هنوز به مدل کلاینت نیاز داشته باشد، سرور نتیجهای با
resultType: "input_required"برمیگرداند و کلاینت درخواست اولیه را همراه با خروجی مدل مجدداً تکرار میکند. با این کار، حلقهی استدلال در لایهی پروتکل صریح، محدود و بیوضعیت (Stateless) میشود.
اهداف یادگیری
- دلیل منسوخشدن Sampling در نسخهی 2026-07-28 پروتکل MCP را توضیح دهید و یکپارچهسازی مستقیم با مدل را بهعنوان گزینهی پیشفرض سرورهای جدید برگزینید.
- یک گردش کار سازگاری بسازید که
sampling/createMessageرا از طریق درخواستهای چند دوررفت (Multi Round-Trip Requests یا MRTR) منتقل کند. - نسخهی بازنگری پروتکل و قابلیتهای کلاینت را در آبجکت
_metaدر تمام درخواستها قرار دهید. - مقدار
resultType: "input_required"را بازگردانید و متد اصلی را با شناسهی JSON-RPC جدید دوباره اجرا کنید. - از تمامیت
requestStateمحافظت کنید و آن را به هویت اصلی (Principal)، متد، آرگومانها و زمان انقضا مقید نمایید. - حلقههای متکی به مدل را با بررسی قابلیتها، تاییدیه، اعتبارسنجی پاسخ و محدودیت تعداد دوررفت مهار کنید.
تصمیمگیری پیش از ورود به پروتکل
ابزاری مانند summarize_repo به دو نوع کار نیاز دارد:
- کارهای قطعی: فهرست کردن فایلها، خواندن فایلهای مجاز، اعتبارسنجی مسیرها و سرهمبندی محتوا.
- کارهای مدلی: انتخاب فایلهای شاخص و ترکیب آنها برای ساخت خلاصه.
اکنون دو معماری معتبر پیش روی شماست:
سرور جدید: یکپارچهسازی مستقیم با ارائهدهندهی مدل
این گزینه پیشفرض فعلی است. سرور مسئولیت انتخاب مدل، اطلاعات هویتی (Credentials)، بودجهها، تلاشهای مجدد و رصدپذیری را بر عهده میگیرد و در نهایت یک نتیجهی معمولی tools/call را به کلاینت MCP برمیگرداند.
این مسیر را زمانی انتخاب کنید که سرور از قبل یک سرویس میزبانیشده باشد، یا رفتار قابلپیشبینی مدل برایتان مهمتر از استفاده از مدل برنامهی میزبان است.
گردش کار فعلی Sampling: مهاجرت به MRTR
Sampling در دورهی گذار تا حذف کامل، همچنان فعال است. سروری که نسخهی 2026-07-28 را هدف میگیرد، نمیتواند یک درخواست زندهی sampling/createMessage را در پاسخ به کلاینت بفرستد؛ در عوض آن درخواست را درون یک InputRequiredResult میگنجاند.
این مسیر سازگاری را فقط زمانی برگزینید که استفاده از مدل و اطلاعات هویتی کلاینت یک الزام واقعی محصول باشد. حتماً برنامهای برای حذف تدریجی آن در نظر بگیرید؛ چرا که پیادهسازیهای جدید نباید بهسراغ Sampling منسوخشده بروند.
قرارداد بیوضعیت
پروتکل ژوئیهی 2026 فرآیند تبادل initialize، اعلانهای notifications/initialized و هدر Mcp-Session-Id ندارد. هر درخواست حامل همان اطلاعاتی است که قبلاً در فرآیند دستدادن (Handshake) ردوبدل میشد:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "summarize_repo",
"arguments": {"audience": "developer"},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {"sampling": {}},
"io.modelcontextprotocol/clientInfo": {
"name": "lesson-client",
"version": "1.0.0"
}
}
}
}
سرور نسخهی پروتکل را در تکتک درخواستها بررسی میکند. نبودِ نسخه یا ارائهی مقداری غیر از رشته، خطای پارامترهای نامعتبر با کد 32602- را در پی دارد. ارسال رشتهای پشتیبانینشده خطای 32022- را با ساختار دادهی دقیق {"supported":["2026-07-28"],"requested":"<client version>"} برمیگرداند. در صورت نبود قابلیت Sampling، سرور خطای 32021- را همراه با data.requiredCapabilities برابر با {"sampling":{}} صادر میکند.
پیامی که شناسهی id در ساختار JSON-RPC نداشته باشد، یک اعلان تلقی میشود. گیرنده ممکن است آن را پردازش کند، اما هیچ پاسخ موفق یا خطایی نمیفرستد. یک تطبیقدهندهی (Adapter) استریممحور HTTP برای یک اعلان پذیرفتهشده، پاسخ 202 Accepted را بدون بدنه برمیگرداند.
سرور متد server/discover را هم با کلید دقیق supportedVersions، قابلیتها، ttlMs و cacheScope پیادهسازی میکند تا کلاینت پیش از فراخوانی هر ابزاری، قرارداد سرور را دریافت و ذخیره کند. چون بخش Discovery ابزارهای موجود را معرفی میکند، سرور موظف است tools/list را هم پیاده کند. توصیفگر قطعی ابزار summarize_repo شامل یک inputSchema معتبر از نوع آبجکت، resultType: "complete"، متادیتای هویتی سرور و راهنماهای کَش عمومی است.
هر نتیجهی موفق مدرن، یک تفکیککننده دارد:
- مقدار
resultType: "complete"یعنی عملیات به پایان رسیده است. - مقدار
resultType: "input_required"یعنی کلاینت باید درخواستهای درون پیام را انجام دهد و دوباره فراخوانی را بفرستد. - افزونهها ممکن است انواع خروجی دیگری هم تعریف کنند؛ مثلاً افزونهی Tasks در درس 13 خروجی
"task"را اضافه میکند.
یک دوررفت MRTR
سرور هنگام پردازش درخواست نمیتواند کلاینت را صدا بزند؛ در عوض این نتیجه را برمیگرداند:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "input_required",
"inputRequests": {
"pick_files": {
"method": "sampling/createMessage",
"params": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Choose three representative files and return a JSON array."
}
}
],
"systemPrompt": "Return only the requested value.",
"modelPreferences": {
"costPriority": 0.8,
"intelligencePriority": 0.2
},
"maxTokens": 400
}
}
},
"requestState": "opaque-integrity-protected-value"
}
}
کلاینت پشتیبانی از Sampling را بررسی میکند، سیاستهای مدل و تایید کاربر را اعمال مینماید و پاسخ مدل را میگیرد. سپس درخواست جدیدی با یک شناسهی JSON-RPC متفاوت میفرستد:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "summarize_repo",
"arguments": {"audience": "developer"},
"inputResponses": {
"pick_files": {
"role": "assistant",
"content": {
"type": "text",
"text": "[\"README.md\", \"server.py\", \"docs/intro.md\"]"
},
"model": "host-model",
"stopReason": "endTurn"
}
},
"requestState": "opaque-integrity-protected-value",
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {"sampling": {}}
}
}
}
این تلاش مجدد، ادامهی یک نشست قبلی در لایهی پروتکل نیست؛ درخواستی تازه است که همان متد و آرگومانهای اصلی را تکرار میکند، فقط inputResponses مربوط به دور جاری را میافزاید و مقادیر requestState را بایتبهبایت برمیگرداند.
استفاده از الگوی MRTR فقط روی متدهای tools/call، prompts/get و resources/read مجاز است. سرور نباید در متدهای نامرتبط مقدار input_required را برگرداند.
وضعیت در فراخوانیهای چند دوررفته
این درس به دو فراخوانی مدل نیاز دارد:
pick_filesکه یک آرایهی JSON برمیگرداند.summaryکه متن نهایی خلاصه را تولید میکند.
هر درخواست جدید فقط پاسخهای همان دور را حمل میکند؛ بنابراین سرور مرحلهی فعلی و دادههای میانی اعتبارسنجیشده را در requestState بعدی قرار میدهد.
با این مقدار طوری رفتار کنید که گویی تحت کنترل مهاجم است. صرفِ امضا کردن نام مرحله کافی نیست؛ وضعیت را به موارد زیر مقید کنید:
- هویت اصلی احرازشده، نه مقادیر خوداظهاریشده در
clientInfo؛ - متد مبدا؛
- خلاصهی درهمسازی (Digest) آرگومانهای اصلی؛
- زمان انقضای کوتاه؛
- مرحلهی فعلی و مقادیر میانی تاییدشده.
وقتی نیازی به محرمانگی نیست، از HMAC استفاده کنید. اگر کلاینت نباید محتوای وضعیت را بخواند، رمزنگاری احرازشده (Authenticated Encryption) به کار ببرید. هرگونه امضای نامعتبر، مقدار منقضیشده، تغییر هویت اصلی یا تغییر آرگومانها را با خطای 32602- رد کنید.
کلاینت نباید requestState را پردازش کند یا تغییر دهد؛ تنها وظیفهی آن بازگرداندن دقیق همان رشته در درخواست بعدی است.
اولویتهای مدل صرفاً جنبهی راهنمایی دارند
مقادیر costPriority، speedPriority و intelligencePriority ترجیحات کاملاً مستقلی هستند؛ نه توزیع احتمالاتی محسوب میشوند و نه نیازی است که جمع آنها یک شود. کلاینت میتواند آنها را نادیده بگیرد، چرا که سیاستهای مدل تماماً در اختیار کلاینت است.
اگر همچنان از یک گردش کار قدیمی Sampling نگهداری میکنید، مقدار includeContext را روی "none" بگذارید. سایر حالتهای کانتکست ریسک نشت داده را بالا میبرند و خودشان هم منسوخ شدهاند. حداقل کانتکستِ صریحِ لازم را در متن پرامپت بفرستید.
اصول تغییرناپذیر امنیتی
کلاینت، مرز اعتماد (Trust boundary) درخواستهای درونسازیشدهی Sampling محسوب میشود.
- در صورت الزام سیاستها به تایید کاربر، کاری را که سرور از مدل خواسته به او نشان دهید.
- برای دوررفتهای MRTR سقف مشخص کنید؛ وگرنه سرور مخرب میتواند یک حلقهی مصرف هزینه برای مدل ایجاد کند.
- پاسخهای Sampling را پیش از استفاده بهعنوان نام فایل، آدرس URL یا ورودی ابزار، اعتبارسنجی کنید.
- حجم بایتها و توکنها را در هر دوررفت محدود نمایید.
- اگر درخواست ورودی در قابلیتهای اعلامشدهی فعلی کلاینت نیامده بود، آن را رد کنید.
- خروجی مدل را در تصمیمگیریهای مربوط به مجوز و دسترسی دخالت ندهید.
- متد مبدا و کلید درخواست ورودی را ثبت کنید، بدون اینکه پرامپتهای حساس را لاگ نمایید.
مقادیر clientInfo و serverInfo صرفاً متادیتای نمایشی و عیبیابی هستند؛ هرگز از آنها بهعنوان هویت تاییدشده استفاده نکنید.
پیادهسازی عملی
کد code/main.py این فرآیند دو دوررفته را بدون نیاز به هیچ بستهی شخص ثالث پیادهسازی میکند:
- متد
server/discoverکلیدsupportedVersionsرا برمیگرداند، پشتیبانی از ابزارها را اعلام میکند و راهنماهای کَش را ارائه میدهد. - متد
tools/listیک توصیفگر قطعی و کشپذیر ازsummarize_repoبا اسکیمای ورودی شیء برمیگرداند. - متد
tools/callمتادیتای هر درخواست را اعتبارسنجی میکند. - نخستین نتیجه، درخواست
sampling/createMessageرا برای انتخاب فایلها درون پیام میگنجاند. - نخستین تلاش مجدد، نتیجهی مدل را اعتبارسنجی کرده و درخواست دوم را اضافه میکند.
- مقدار
requestStateکه با HMAC محافظت شده، مرحلهی کار را بین درخواستهای مستقل حمل میکند. - نتیجهی نهایی با
resultType: "complete"فرستاده میشود.
مدل ساختگی میزبان باعث تکرارپذیر و قطعی شدن مثال میشود. برای اتصال به یک میزبان واقعی، فقط کافی است تابع fake_host_model را تغییر دهید. ماشین وضعیت (State Machine) سمت سرور باید همواره قطعی و آزمونپذیر باقی بماند.
استفاده از کد
از ریشهی مخزن اجرا کنید:
cd phases/13-tools-and-protocols/11-mcp-sampling/code
python3 main.py
python3 -m unittest discover tests -v
چکپوینتهای مورد انتظار:
- متد Discovery خروجی کاملی همراه با
ttlMsوcacheScopeبرمیگرداند. - بخش Discovery ابزارها همان توصیفگر مرتبشده را همراه با
resultType، هویت سرور و راهنماهای کَش برمیگرداند. - در صورت نبود قابلیتها یا پشتیبانینشدن نسخهها، سرور دادههای خطای دقیق
32021-و32022-را تحویل میدهد. - اعلانهای بدون شناسه هیچ پاسخی در JSON-RPC تولید نمیکنند.
- شناسههای درخواست
[1, 2, 3]هستند که نشان میدهد هر دوررفت MRTR کاملاً مستقل است. - خروجی دو نتیجهی اول از نوع
input_requiredاست. - خروجی نهایی از نوع
completeاست و فایلهای منتخب بههمراه خلاصه را دارد. - تغییر آرگومانهای اصلی در زمان تلاش مجدد، اعتبارسنجی وضعیت درخواست را رد میکند.
بهکارگیری در عمل
فایل outputs/skill-sampling-loop-designer.md حالا یک ابزار طراحی مهاجرت است. این مستند ابتدا بررسی میکند که آیا بهتر است Sampling به نفع اتصال مستقیم به مدل حذف شود یا خیر. در صورت نیاز به حفظ سازگاری، ساختار دوررفتهای MRTR، مقیدسازی وضعیت، فیلتر قابلیتها، بودجه، اعتبارسنجی و برنامهی حذف نهایی را تدوین میکند.
تمرینها
- پاسخ مرحلهی انتخاب فایل را به یک JSON نامعتبر تغییر دهید. مطمئن شوید سرور بهجای اعتماد به خروجی مدل، خطای
32602-را برمیگرداند. - مقدار
audienceرا بین فراخوانی اول و تلاش مجدد تغییر دهید. توضیح دهید چرا وضعیت مهرومومشده مانع استفادهی دوباره در درخواستهای دیگر میشود. - دور سومی اضافه کنید که از میزبان بخواهد خلاصه را نقد کند. خلاصهی قبلی را درون وضعیت امضاشده منتقل کنید و کل فرآیند را روی 3 دوررفت ببندید.
- با جایگزین کردن کالبک ساختگی میزبان با تطبیقدهندهی مدل درون خود سرور، Sampling را حذف کنید. مواردی را که از نظر تایید، پرداخت هزینه و رصدپذیری به دوش سرور میافتند فهرست کنید.
- یک تست انقضا اضافه کنید که مقداری با گذشت 1 ثانیه از سررسیدش به آن پاس داده شود.
اصطلاحات کلیدی
| اصطلاح | معنا در نسخهی 2026-07-28 |
|---|---|
| Sampling | ویژگی منسوخشده برای درخواست تکمیل متن از مدلِ کلاینت |
| MRTR | الگوی تکرار بیوضعیت برای درخواستهای نیازمند ورودی کلاینت |
InputRequiredResult |
خروجی با مقدار resultType: "input_required" |
inputRequests |
نگاشتی از درخواستهای تولید محتوا، پرامپت یا roots با کلیدهای سرور |
inputResponses |
خروجیهای کلاینت در دور جاری با همان کلیدهای inputRequests |
requestState |
وضعیت ناخوانای سرور که کلاینت عیناً آن را برمیگرداند و سرور اصالتش را میسنجد |
resultType |
فیلد تفکیککنندهی الزامی در خروجیهای نسخههای جدید پروتکل MCP |
| Direct model integration | گزینهی پیشنهادی برای سرورهای جدیدی که به پردازشهای مدل نیاز دارند |
| Capability gate | قانونی که جلوی فرستادن درخواستهای خارج از تواناییهای اعلامشدهی کلاینت را میگیرد |
| Loop budget | حداکثر تعداد دوررفت، توکن، بایت، زمان و هزینهی مجاز برای عملیات |
سازگاری با گذشته
کلاینتی که روی نسخهی 2025-11-25 مانده باشد، همچنان میتواند از جریان قدیمی sampling/createMessage با شروع از سمت سرور روی یک اتصال زنده استفاده کند. این رفتار را فقط در یک تطبیقدهندهی ویژهی همان نسخه نگه دارید. معماری سرور نسخهی 2026-07-28 را به این مسیرهای وابسته به نشست گره نزنید.
کتابخانههای رسمی SDK میتوانند مدیریتکنندههای مدرن input_required را برای نسخههای قدیمی بازنویسی کنند. این لایهی تطبیق (Shim) صرفاً راهکاری برای سازگاری است؛ نه بهانهای برای نوشتن منطقهای وابسته به نشست جدید.
