در پروتکل MCP (نسخهی 2026-07-28) تمام سشنها، هدر Mcp-Session-Id و متدهای متصل به نشست حذف شدهاند و بستر ارتباطی کاملاً بیوضعیت (stateless) عمل میکند. با این حال در دنیای واقعی، بسیاری از ابزارها مانند گزارشگیریهای سنگین، تولید دادههای حجیم یا کارهایی که در میانه به تأیید کاربر نیاز دارند، نمیتوانند در قالب مهلت زمانی یک درخواست استاندارد HTTP یا stdio تمام شوند.
افزونهی رسمی io.modelcontextprotocol/tasks این چالش را بدون بازگرداندن سشن به هستهی پروتکل حل میکند. در این رویکرد، سرور یک دستگیرهی ماندگار (task id) تولید میکند که روی یک ذخیرهساز مشترک ذخیره میشود؛ بنابراین کلاینت میتواند حتی پس از ریاستارت شدن، وضعیت تسک را از هر نمونهی سرور استعلام کرده یا ورودیهای تکمیلی را ارسال کند.
در این درس یاد میگیرید که چگونه چرخه حیات کارهای طولانیمدت را با استفاده از متدهای tasks/get، tasks/update و tasks/cancel پیاده کنید، متدهای منسوخ قدیمی را کنار بگذارید و سیستم را در برابر ناپایداریهای شبکه و ریاستارت سرویسها مقاوم بسازید.
پروتکل MCP بیوضعیت (Stateless) است، اما این به معنای پایان اجباری تمام عملیات در قالب یک درخواست نیست. افزونهی رسمی Tasks دستگیرهای (handle) ماندگار و صریح برای کارهای طولانیمدت فراهم میکند. سرور میتواند این دستگیره را در پاسخ
tools/callبرگرداند، هر نمونهای از سرور میتواند بهtasks/getپاسخ دهد و ورودی کلاینت نیز از طریقtasks/updateدریافت میشود، بدون اینکه نیازی به زنده کردن سشنهای پروتکل باشد.
اهداف یادگیری
- تشخیص تفاوت بستر انتقال پروتکل بیوضعیت از وضعیت ماندگار تسک در سطح برنامه.
- توافق بر سر قابلیت افزونهی
io.modelcontextprotocol/tasksدر قابلیتهای هر درخواست وserver/discover. - بازگرداندن
CreateTaskResultهدایتشده از سمت سرور باresultType: "task"تنها پس از ایجاد ماندگار تسک. - نظرسنجی دورهای (Poll) با
tasks/get، تأمین ورودی تسک باtasks/updateو درخواست لغو هماهنگ (cooperative cancellation) باtasks/cancel. - کنار گذاشتن فرضیات قدیمی دربارهی متدهای
tasks/status،tasks/resultوtasks/list. - عضویت در اعلانات اختیاری تسک از طریق
subscriptions/listenروی جریان SSE در پاسخ POST. - مدلسازی صحیح انقضای تسک، بازیابی پس از راهاندازی مجدد، حذف دادههای تکراری در کلیدهای ورودی و خطاهای اجرایی.
چرا تسکها بهشکل افزونه ارائه شدهاند؟
تسکها نخستین بار در تاریخ 2025-11-25 بهعنوان قابلیتی آزمایشی در هستهی پروتکل ظاهر شدند. در بازطراحی جولای 2026، آنها به افزونهی رسمی io.modelcontextprotocol/tasks منتقل شدند تا کلاینتها و سرورها بتوانند این چرخهی حیات مازاد را در صورت تمایل انتخاب کنند، بدون اینکه هستهی پروتکل برای همه سنگینتر شود.
مشخصات این افزونه همچنان در وضعیت پیشنویس (draft) قرار دارد، هرچند اکنون مرجع رسمی تسکها به شمار میرود. نسخهی افزونهی موردپشتیبانی در SDK خود را قفل (pin) کنید، سناریوهای انطباق را اجرا کنید و تطبیقدهندههای ارتباطی (wire adapters) را از دامنهی ذخیرهسازی و ورکرها جدا نگه دارید.
تنها زمانی از تسک استفاده کنید که عملیات دستکم یکی از ویژگیهای زیر را داشته باشد:
- زمان اجرای آن ممکن است از مهلت مجاز یک درخواست معمولی (timeout) فراتر رود.
- صف ورکر (worker queue) یا سیستم جاب خارجی مدیریت اجرا را بر عهده دارد.
- کلاینت باید بتواند پس از ریاستارت خود، کار را بازیابی کند.
- عملیات حین اجرا منتظر دریافت ورودی از کاربر یا مدل متوقف میشود.
- لغو عملیات و دریافت ماندگار نتیجه از الزامات محصول است.
برای یک جستوجوی قطعی و سبک تسک نسازید. دستگیره، ماندگاری، نظرسنجی دورهای، انقضا و لغو، پیچیدگی واقعی به همراه دارند.
هستهی بیوضعیت، برنامهی باوضعیت
نسخهی 2026-07-28 پروتکل MCP متدهای initialize، notifications/initialized، سشنهای پروتکل و هدر Mcp-Session-Id را حذف کرده است. این موضوع مانع ساخت محصولات باوضعیت نمیشود.
شناسهی تسک (task id) یک وضعیت صریح در سطح برنامه است:
- سرور پیش از بازگرداندن شناسه، آن را ماندگار میکند.
- کلاینت میتواند آن را ذخیره کرده و پس از راهاندازی مجدد، وضعیتش را بررسی کند.
- این شناسه قابلیت هدایت به هر نمونهای از سرور که متصل به همان ذخیرهساز مشترک است را دارد.
- مجوز دسترسی در تکتک متدهای تسک بررسی میشود.
- زمان انقضا و حذف با فیلدهای خود تسک تعریف میشوند، نه با طول عمر بستر انتقال.
این ساختار از نظر عملیاتی با وضعیت پنهان متصل به یک کانکشن کاملاً تفاوت دارد.
این چهار چرخهی حیات را کاملاً تفکیکشده نگه دارید:
| وضعیت | طول عمر | محل نگهداری |
|---|---|---|
| فرادادهی پروتکل | یک درخواست | params._meta، که در هر فراخوانی مجدداً اعتبارسنجی میشود |
| پردازش بستر انتقال | یک درخواست stdio یا پاسخ HTTP | هماهنگکنندهی در حال اجرا با ضربالاجل مشخص |
| ادامهی MRTR | یک توالی تلاش مجدد | پارامتر requestState حفاظتشده از نظر یکپارچگی، همراه با کنترلهای بازپخش در صورت نیاز |
| تسک ماندگار | در میان درخواستها، نمونههای سرور، ریاستارتها و اتصالات مجدد | ذخیرهساز مشترک برنامه با کلید شناسهی مجاز taskId |
نگهداری دادههای تسک در حافظهی پردازه (process memory) به معنای باوضعیت شدن MCP نیست، بلکه برنامه را غیرقابلاعتماد میکند. پروتکل بیوضعیت باقی میماند، اما فراخوانی بعدی tasks/get که به نمونهی دیگری هدایت شود نمیتواند اطلاعات را پیدا کند. اطلاعات را پیش از تحویل دستگیره ذخیره کنید، سپس طوری پیادهسازی کنید که تمام متدهای تسک با بررسیهای امنیتی مستاجر (tenant) و هویت اصلی (principal)، به همان رکورد مشترک دسترسی یابند.
توافق بر سر قابلیتها
کلاینت پشتیبانی خود را در هر درخواستی که شرایط لازم را دارد اعلام میکند:
{
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
},
"io.modelcontextprotocol/clientInfo": {
"name": "lesson-client",
"version": "1.0.0"
}
}
}
سرور مقادیر دقیق supportedVersions، قابلیتها، ttlMs و cacheScope را از server/discover برمیگرداند و همان افزونه را ذیل capabilities قرار میدهد. چون ابزارها را اعلام کرده، پیادهسازی متد اجباری tools/list نیز الزامی است. این خروجی شامل توصیفگر قطعی generate_report، ساختار شیء معتبر inputSchema، مقدار resultType: "complete"، فرادادهی هویت سرور و راهنماهای کش عمومی (public cache hints) است.
اگر کلاینتی افزونه را اعلام نکرده باشد و متد تسک را فراخوانی کند، خطای 32021- (Missing Required Client Capability) را همراه با مقدار data.requiredCapabilities بهشکل {"extensions":{"io.modelcontextprotocol/tasks":{}}} دریافت میکند. ارسال رشتهی پروتکل پشتیبانینشده خطای 32022- با دادههای دقیق supported و requested برمیگرداند؛ نسخهی ناموجود یا غیررشتهای خطای 32602- میدهد.
پیامی بدون id در JSON-RPC یک اعلان (notification) محسوب میشود. گیرنده میتواند آن را پردازش کند، اما هیچ پاسخ یا خطایی در JSON-RPC صادر نمیکند. تطبیقدهندهی Streamable HTTP برای اعلان پذیرفتهشده پاسخ 202 Accepted بدون بدنه برمیگرداند.
در حال حاضر فقط tools/call از اجرای مبتنی بر افزایش داده با تسک پشتیبانی میکند. انتزاع داخلیتان را طوری طراحی کنید که اضافهشدن انواع درخواستهای جدید در آینده نیازی به بازنویسی بخش ذخیرهسازی نداشته باشد.
ایجاد تسک با هدایت سرور
فلگ قدیمی کلاینت یعنی params._meta.task.required حذف شده است. اکنون کلاینت پشتیبانی از افزونه را اعلام میکند، سپس سرور تصمیم میگیرد که آیا یک tools/call خاص به تسک تبدیل شود یا خیر.
درخواست:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "generate_report",
"arguments": {"size": "large"},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}
پاسخ:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "task",
"taskId": "tsk_786512e29e0d",
"status": "working",
"statusMessage": "Preparing report outline.",
"createdAt": "2026-08-21T10:30:00Z",
"lastUpdatedAt": "2026-08-21T10:30:00Z",
"ttlMs": 900000,
"pollIntervalMs": 1000
}
}
سرور نباید این دستگیره را قبل از اینکه شناسه در tasks/get قابلبازیابی باشد برگرداند. در پایگاههای داده با سازگاری نهایی (eventual consistency)، پیش از پاسخ دادن منتظر قابلمشاهده شدن خواندن بمانید؛ در غیر این صورت ممکن است کلاینت شناسهای ظاهراً معتبر دریافت کند و بلافاصله با خطای «یافت نشد» مواجه شود.
پاسخ مبتنی بر تسک از این جهت ناخواسته (unsolicited) است که کلاینت مستقیماً حالت تسک را درخواست نکرده است. با این حال، بیتوافق قبلی نیست؛ زیرا درخواست فعلی همچنان باید افزونه را اعلام کرده باشد.
ساختار تسک
هر تسک شامل این موارد است:
- مقدار
taskId: شناسهی پایدار تولیدشده توسط سرور؛ - مقدار
status: یکی از وضعیتهایworking،input_required،completed،cancelledیاfailed؛ - مقادیر
createdAtوlastUpdatedAt: برچسبهای زمانی با استاندارد ISO 8601؛ - مقدار
ttlMs: مدتزمان انقضا از زمان ایجاد، یاnullدر صورتی که محدودیتی اعلام نشده باشد؛ - فیلد اختیاری
pollIntervalMs: حداقل فاصلهی زمانی پیشنهادی سرور برای نظرسنجی دورهای؛ - فیلد اختیاری
statusMessage: توضیحات مناسب برای نمایش به کاربر یا مدل.
فیلدهای مربوط به هر وضعیت تنها در زمان لازم ظاهر میشوند:
- وضعیت
input_requiredشاملinputRequestsاست. - وضعیت
completedساختارresultدرخواست اصلی را در بر دارد. - وضعیت
failedحاوی شیءerrorدر استاندارد JSON-RPC است.
کلاینت باید pollIntervalMs را رعایت کند. سرور ممکن است درخواستهای مکررتر را محدود (rate-limit) کند یا این فاصله را در طول حیات تسک تغییر دهد.
نظرسنجی دورهای با tasks/get
کلاینت اسنپشات فعلی را درخواست میکند:
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/get
Mcp-Name: tsk_786512e29e0d
{
"jsonrpc": "2.0",
"id": 2,
"method": "tasks/get",
"params": {
"taskId": "tsk_786512e29e0d",
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}
خود فراخوانی tasks/get کامل شده، بنابراین فیلد نتیجهی آن همیشه resultType: "complete" است. با این حال، وضعیت تسک داخلی میتواند همچنان status: "working" یا status: "input_required" باشد.
این تفکیک مانع یک باگ رایج در پارسرها میشود:
result.resultType = complete means the tasks/get RPC finished
result.status = working means the represented job is still running
چیزی بهنام فراخوانی tasks/result وجود ندارد. با پایان کار تسک، پاسخ tasks/get بعدی خروجی اصلی CallToolResult را درون فیلد result بهصورت درونخطی قرار میدهد:
{
"resultType": "complete",
"taskId": "tsk_786512e29e0d",
"status": "completed",
"createdAt": "2026-08-21T10:30:00Z",
"lastUpdatedAt": "2026-08-21T10:34:12Z",
"ttlMs": 900000,
"result": {
"resultType": "complete",
"content": [
{"type": "text", "text": "Generated large report with approved outline."}
],
"structuredContent": {"size": "large", "approved": true},
"isError": false,
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "tasks-demo",
"version": "1.0.0"
}
}
},
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "tasks-demo",
"version": "1.0.0"
}
}
}
مقدار resultType بیرونی نشان میدهد که RPC مربوط به tasks/get کامل شده است. مقدار تفکیککنندهی درون result.resultType نیز بیانگر تکمیل فراخوانی ابزار اصلی است. وجود این فیلد داخلی الزامی است. همچنین شیء درونی CallToolResult باید فرادادهی io.modelcontextprotocol/serverInfo اختصاصی خود را داشته باشد؛ در این درس بهجای ذخیرهی دادهی بدوننوع، این متادیتا را نیز نگهداری میکنیم.
متد tasks/list وجود ندارد. سرورهای بدون سشن نمیتوانند با اطمینان تشخیص دهند چه تسکهایی متعلق به فهرستی در دامنهی یک کانکشن خاص هستند. برنامههایی که به تاریخچه نیاز دارند باید ابزاری در سطح دامنهی خود پیادهسازی کنند که فیلترها و قوانین مالکیت صریحی داشته باشد.
دریافت ورودی حین اجرای تسک
دریافت ورودی در تسک و الگوی هستهی MRTR شبیه به هم هستند اما از روشهای ادامهی متفاوتی استفاده میکنند.
نیاز به ورودی پیش از ایجاد تسک
از همان tools/call اولیه، پاسخ resultType: "input_required" مربوط به هسته را برگردانید. کلاینت داده را پر میکند و همان فراخوانی اصلی را دوباره میفرستد. تنها پس از پایان این نوبتهای همگام MRTR، تسک را ایجاد کنید.
نیاز به ورودی پس از ایجاد تسک
وضعیت تسک را به input_required تغییر دهید. متد tasks/get موارد inputRequests باز را اعلام میکند و کلاینت پاسخها را از طریق tasks/update میفرستد. کلاینت دیگر درخواست اولیهی tools/call را تکرار نمیکند.
اسنپشات:
{
"resultType": "complete",
"taskId": "tsk_786512e29e0d",
"status": "input_required",
"createdAt": "2026-08-21T10:30:00Z",
"lastUpdatedAt": "2026-08-21T10:31:00Z",
"ttlMs": 900000,
"inputRequests": {
"approve_outline": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Approve the generated report outline?",
"requestedSchema": {
"type": "object",
"properties": {"approved": {"type": "boolean"}},
"required": ["approved"]
}
}
}
}
}
بهروزرسانی:
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/update
Mcp-Name: tsk_786512e29e0d
{
"jsonrpc": "2.0",
"id": 4,
"method": "tasks/update",
"params": {
"taskId": "tsk_786512e29e0d",
"inputResponses": {
"approve_outline": {
"action": "accept",
"content": {"approved": true}
}
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}
پاسخ موفقیتآمیز شامل یک تأییدیهی خالی بههمراه resultType: "complete" است. از آنجا که تغییر وضعیت ممکن است با تاخیر اعمال شود (eventually consistent)، کلاینت به نظرسنجی دورهای یا گوش دادن ادامه میدهد.
هر کلید در inputRequests باید در کل طول حیات تسک یکتا باشد. اسنپشاتهای پیاپی tasks/get ممکن است همان کلید باز را نشان دهند؛ کلاینتها موارد تکراری را در رابط کاربری فیلتر میکنند و سرورها پاسخهای ارائهشده برای کلیدهای نامعتبر، منسوخ یا قبلاً تأمینشده را نادیده میگیرند. اگر بهروزرسانی ناقص باشد، تسک تا زمان پاسخ به همهی کلیدهای لازم در حالت input_required باقی میماند.
لغو هماهنگ عملیات
متد tasks/cancel نیت لغو را اعلام کرده و پاسخی خالی مبنی بر تکمیل عملیات برمیگرداند. این تأییدیه تضمین نمیکند که ورکر کار خود را متوقف کرده باشد؛ ممکن است پردازش زودتر تمام شود، درخواست لغو نادیده گرفته شود یا تغییر وضعیت دیرتر رخ دهد.
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tasks/cancel
Mcp-Name: tsk_786512e29e0d
{
"jsonrpc": "2.0",
"id": 5,
"method": "tasks/cancel",
"params": {
"taskId": "tsk_786512e29e0d",
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
}
}
در هر سه متد تسک، مقدار هدر Mcp-Name با params.taskId برابر است و نام متد JSON-RPC را تکرار نمیکند. در code/main.py این منطق درون تابع make_http_request متمرکز شده است.
ورکر این درس لغو را بلافاصله اعمال میکند، بنابراین فراخوانیهای مکرر تأثیری مشابه دارند (idempotent). اما در کلاینت محیط عملیاتی باید لغو را همواره هماهنگ در نظر گرفت، نه اینکه پس از دریافت تأییدیه وضعیت نهایی تسک را قطعی دانست.
از notifications/cancelled برای لغو تسک استفاده نکنید. آن اعلان برای لغو درخواستها است، نه تسکهای ماندگار.
این تفاوت در مرز مسیریابی اهمیت بالایی دارد. لغو درخواست فقط یک عملیات در حال پردازش JSON-RPC یا پاسخ HTTP در دامنهی درخواست را هدف میگیرد. اگر tools/call قبلاً resultType: "task" را برگردانده باشد، کار آن درخواست تمام شده است و بستن بستر انتقال نمیتواند کار ماندگار را شناسایی یا متوقف کند. متد tasks/cancel یک فراخوانی RPC جدید و احرازشده است که با ارائهی params.taskId، قرار دادن همان شناسه در Mcp-Name، یافتن بکاند صاحب تسک و ثبت نیت لغو هماهنگ، تأییدیه برمیگرداند بدون اینکه ادعا کند کار متوقف شده است.
بنابراین، گیتوی باید هماهنگکنندههای درخواست و مسیرهای تسک را در جدولهای جداگانهای نگه دارد. جدول درخواستها پس از پایان پاسخ پاک میشود، اما مسیر تسک باید تا رسیدن به وضعیت نهایی و سررسید انقضا پابرجا بماند. در درس 29: قابلیت اطمینان، لغو و کنترل جریان در MCP، قوانین رقابت در پردازش (race conditions)، مهلت زمانی، تکرارپذیری، پسزدگی (backpressure) و تلاش مجدد برای هر دو مسیر تشریح شده است.
اعلانات اختیاری
نظرسنجی دورهای خط مبنا است. کلاینتی که بهروزرسانیهای push میخواهد، متد subscriptions/listen را همراه با شناسههای تسک ارسال میکند. در پروتکل Streamable HTTP، این کار با یک درخواست POST انجام میشود که پاسخ آن یک جریان SSE در دامنهی همان درخواست است. جریان رویداد مستقل از نوع GET یا سشن پروتکل برای فعال نگهداشتن وجود ندارد.
سرور شناسههای پذیرفتهشده را با اعلان notifications/subscriptions/acknowledged تأیید میکند و پس از آن میتواند اسنپشاتهای کامل را از طریق notifications/tasks بفرستد. این تأییدیه و تمامی اعلانات تسک، فیلد io.modelcontextprotocol/subscriptionId را در بخش _meta و برابر با شناسهی درخواست subscriptions/listen قرار میدهند. سایر محتویات اعلان تسک دقیقاً معادل خروجی tasks/get در همان لحظه است.
کلاینتها همچنان موظفاند افزونهی Tasks را اعلام کنند. همچنین باید بتوانند با تکیه بر شناسهی ماندگار تسکها اتصال خود را مجدداً برقرار کنند، نه اینکه به بازپخش رویدادها یا هدر Last-Event-ID وابسته باشند.
رفتارشناسی خطاها
دو لایهی خطا را درست به کار ببرید:
خطای پروتکل
پارامترهای متد نامعتبر یا شناسهی تسک ناشناخته خطای JSON-RPC برمیگردانند که معمولاً 32602- است. اعلام نکردن افزونه خطای 32021- همراه با شیء قابلیتهای الزامی را به دنبال دارد.
نتیجهی اجرای تسک
- نتیجهی عادی ابزار که فیلد
isError: trueدارد همچنان یک تسکcompletedمحسوب میشود، زیرا فراخوانی ابزار به نتیجهی تعریفشدهی خود رسیده است. - خطای JSON-RPC هنگام اجرای معوق، تسک را به
failedتبدیل کرده و همان خطای JSON-RPC را در فیلدerrorذخیره میکند. - رد درخواست توسط کاربر میتواند به وضعیت
cancelled، نتیجهی تکمیلشدهی حاوی پیام انصراف یا خروجی امن دیگری متناسب با منطق برنامه ختم شود. این تصمیم را مستند کنید.
ماندگاری، انقضا و مالکیت
حداقل موارد زیر را ذخیره و ماندگار کنید: شناسهی تسک، وضعیت، برچسبهای زمانی، زمان انقضا (TTL)، فاصلهی نظرسنجی، مالکیت عملیات اصلی، نتیجه یا خطا، درخواستهای ورودیِ باز و تمام کلیدهای ورودی صادرشده.
کلید ذخیرهسازی باید شامل اطلاعات مستاجر و هویت اصلی معتبر باشد یا بتواند آنها را مشخص کند. دانستن شناسهی تسک نباید مجوز دسترسی به آن تلقی شود. در هر بار اجرای tasks/get، tasks/update، tasks/cancel و ایجاد عضویت، مالکیت را بررسی کنید.
مقدار ttlMs از زمان ایجاد تسک محاسبه میشود و ممکن است تغییر کند. کلاینت میتواند از آن بهعنوان مرزی برای تشخیص تسکهایی که دیگر بهروزرسانی تولید نمیکنند استفاده کند. سرور میتواند تسک منقضیشده را ناموفق علامت زده و بعداً آن را حذف کند. این مقدار را تعهدی برای نگهداری نتیجهی تسک تکمیلشده به همان میزان میلیثانیه پس از تکمیل تلقی نکنید.
عملیات نوشتن را اتمیک (atomic) یا در قالب تراکنش انجام دهید. در کد این درس ابتدا یک فایل موقت نوشته شده و سپس بهصورت اتمیک تغییر نام پیدا میکند. یک سرویس با چند نمونهی همزمان (multi-replica) باید از ذخیرهساز مشترک ماندگار و اجارهی ورکر (worker lease) یا روشهای هماهنگی مشابه استفاده کند.
پیادهسازی
فایل code/main.py یک سرویس تسک قطعی را پیادهسازی میکند:
- متد
server/discoverفیلدهایsupportedVersions، راهنماهای کش و افزونهی Tasks را برمیگرداند. - متد
tools/listتوصیفگر قطعی و قابل کشgenerate_reportرا با اسکیمای ورودی معتبر برمیگرداند. - متد
tools/callتسک را پیش از بازگرداندنresultType: "task"ایجاد و ماندگار میکند. - نمونهی جدیدی از سرویس همان تسک را بارگذاری میکند تا بازیابی پس از ریاستارت را نشان دهد.
- متد
tasks/getاسنپشاتهای کامل تسک را برمیگرداند. - ورکر وضعیت را از
workingبهinput_requiredتغییر میدهد. - متد
tasks/updateپاسخ فرم را دریافت کرده و تأییدیهی کامل و خالی برمیگرداند. - ورکر ساختار تودرتوی
CallToolResultرا همراه باresultTypeاختصاصی و هویت سرور ذخیره کرده و بهcompletedمیرود. - متد
tasks/cancelدر این پیادهسازی idempotent است. - سازندهی درخواست HTTP مقدار هدر
Mcp-Nameرا برای متدهایtasks/get،tasks/updateوtasks/cancelبرابر باparams.taskIdقرار میدهد. - توابع کمکی اعلانات از
notifications/subscriptions/acknowledgedوnotifications/tasksاستفاده میکنند و هر دو را با شناسهی درخواست گوشدادن برچسب میزنند. - اعلانهای فاقد شناسه، هیچ پاسخی در فرمت JSON-RPC تولید نمیکنند.
ورکر بهجای متوقف کردن نخ پردازشی در پسزمینه، با فراخوانی صریح گامها جلو میرود. این کار باعث میشود تمام تغییرات وضعیت قطعی باشند و منطق پروتکل از سازوکارهای صف جدا بماند.
استفادهی عملی
از ریشهی مخزن اجرا کنید:
cd phases/13-tools-and-protocols/13-mcp-async-tasks/code
python3 main.py
python3 -m unittest discover tests -v
توالی خروجی مورد انتظار:
id=0 resultType=complete status=ack
id=1 resultType=task status=working
id=2 resultType=complete status=working
id=3 resultType=complete status=input_required
id=4 resultType=complete status=ack
id=5 resultType=complete status=completed
همچنین بررسی کنید که متدهای tasks/status، tasks/result و tasks/list در سرویس امروزی خطای method-not-found برگردانند.
مطمئن شوید خروجی tools/list قطعی است و تمام متدهای کنونی تسک در پروتکل HTTP شناسهی تسک را در هدر Mcp-Name منعکس میکنند.
آمادهسازی نهایی
فایل outputs/skill-task-store-designer.md اکنون حاوی طراحی منطبق بر این افزونه است: توافق بر سر قابلیتها، ماندگاری پیش از بازگرداندن شناسه، متدهای روزآمد، جریان بهروزرسانی ورودی، مالکیت، انقضا، لغو، عضویت در اعلانات و مسیر مهاجرت از متدهای آزمایشی قدیمی.
تمرینها
- یک کلید ورودی باز دیگر به تسک اضافه کنید. با ارسال یک
tasks/updateناقص نشان دهید که تسک تا زمان تکمیل هر دو کلید در وضعیتinput_requiredباقی میماند. - تفکیک مستاجرها را به ذخیرهساز اضافه کنید و در صورت ارائهی شناسهی تسک معتبر توسط هویت اصلی احرازشدهی نادرست، آن را رد کنید.
- یک worker lease منقضیشونده اضافه کنید. نشان دهید که دو نمونه از سرویس نمیتوانند یک تسک مشترک را بهطور همزمان تکمیل کنند.
- یک تطبیقدهندهی SSE بر پایهی پاسخ POST برای متد
subscriptions/listenپیاده کنید؛ بدون استفاده از GET، هدرLast-Event-IDیا هدر سشن. - روال پاکسازی تسکهای منقضی را پیاده کنید؛ بهنحویکه تسک منقضیشده از شناسهی تسک نامعتبر تشخیص داده شود، بدون اینکه اطلاعاتی از وجود تسکهای مستاجران دیگر به بیرون درز کند.
اصطلاحات کلیدی
| اصطلاح | مفهوم در افزونهی فعلی |
|---|---|
| افزونهی Tasks | قابلیت اختیاری io.modelcontextprotocol/tasks برای کارهای ماندگار ناهمگام |
CreateTaskResult |
پاسخ با resultType: "task" هدایتشده از سمت سرور به یک درخواست واجد شرایط |
tasks/get |
نظرسنجی دورهای برای اسنپشات کامل فعلی تسک، شامل نتیجهی نهایی یا ورودیهای در انتظار |
tasks/update |
ارسال پاسخ برای موارد inputRequests تکمیلنشده در تسک |
tasks/cancel |
اعلام دریافت نیت لغو هماهنگ عملیات |
input_required |
وضعیت تسک که نشان میدهد پردازش منتظر ورودی از سمت کلاینت است |
pollIntervalMs |
حداقل زمان پیشنهادی سرور پیش از اجرای نظرسنجی دورهای بعدی |
ttlMs |
مدتزمان اعتبار تسک که از لحظهی ایجاد سنجیده میشود |
| ماندگاری پیش از بازگرداندن | الزام به ذخیره شدن شناسهی تسک پیش از ارسال دستگیرهی آن به کلاینت |
notifications/tasks |
اسنپشات کامل اختیاری از تسک که در جریان SSE عضویت ارسال میشود |
سازگاری با نسخههای قدیمی
طراحی آزمایشی 2025-11-25 از سیستم افزایش داده با تسک به درخواست کلاینت و متدهای tasks/status، tasks/result و متد اختیاری tasks/list استفاده میکرد. این نامها را فقط درون یک تطبیقدهندهی قدیمی و ایزولهشده نگه دارید. کلاینت کنونی از قابلیت افزونه استفاده میکند، دستگیرههای هدایتشده توسط سرور را میپذیرد، با tasks/get نظرسنجی دورهای انجام میدهد، ورودی را با tasks/update میفرستد و نتیجهی نهایی را مستقیماً از اسنپشات تسک میخواند.
