ai-ml

إضافة MCP Tasks: خادم TypeScript يعمل (2026)

١٨ سبتمبر ٢٠٢٦

MCP Tasks Extension: A Working TypeScript Server (2026)

تسمح إضافة MCP Tasks (io.modelcontextprotocol/tasks)، التي تم إقرارها في مواصفات 2026-07-28، باستدعاء الأداة لإرجاع مقبض (handle) مستدام بدلاً من حظر التنفيذ: حيث تقوم العملاء بعمل poll لـ tasks/get، والإجابة على المدخلات في منتصف المهمة عبر tasks/update، والإلغاء باستخدام tasks/cancel12. لا توجد حزمة SDK رسمية من TypeScript تطبق هذا الشكل بدقة حتى الآن.

هذه الفجوة ليست واضحة من الوثائق. حتى وقت كتابة هذا المقال، لا يزال دعم المهام المدمج في @modelcontextprotocol/sdk يتحدث بإصدار قديم سابق للإقرار، وحزم v2 التي تم تقسيمها حديثاً لا تطبق Tasks على الإطلاق في مفردات النقل النشطة الخاصة بها — بل تحتفظ فقط بهذا الإصدار القديم نفسه كأنواع ميتة غير تشغيلية (non-runtime types). يقوم هذا المنشور ببناء خادم وفقاً للمواصفات المقرة يدوياً، ويتحقق منه من البداية إلى النهاية مقابل عميل حقيقي، ويوضح بالضبط أين يختلف الـ SDK المتاح.

ملخص

تحتوي إضافة MCP Tasks المقرة على ثلاث طرق — tasks/get، و tasks/update، و tasks/cancel — ولا توجد tasks/list، وذلك عن قصد لأسباب أمنية1. لا يزال موديول experimental/tasks في إصدار @modelcontextprotocol/sdk v1.30.0 يطبق شكلاً سابقاً يحتوي على tasks/list و tasks/result، ولا يحتوي على tasks/update على الإطلاق3؛ أما حزم @modelcontextprotocol/{core,server,client} v2.0.0 الجديدة فلا تطبق أي طرق Tasks في مفردات النقل النشطة الخاصة بها على الإطلاق، وتحتفظ بذلك الشكل السابق فقط كصادرات لأنواع ميتة غير تشغيلية4. الحزمة الوحيدة المنشورة رسمياً والتي تطابق المواصفات المقرة، وهي @modelcontextprotocol/ext-tasks، مخصصة لجانب الطالب (requester-side) فقط5. يقوم هذا المنشور ببناء خادم صحيح وفقاً للمواصفات يدوياً باستخدام كلاس Server منخفض المستوى في الـ SDK، ويقوم بتشغيله مقابل عميل حقيقي، ويستعرض خطأً حقيقياً اكتشفته هذه العملية: إرجاع مقبض setTimeout شارد في سجل المهمة يؤدي إلى تعليق العميل بصمت، لأن وسيلة النقل (transport) لا يمكنها تحويله إلى سلسلة نصية (serialize) ولا تبلغ أبداً عن السبب.

ما ستتعلمه

  • الأشكال الدقيقة لنقل البيانات في tasks/get و tasks/update و tasks/cancel وفقاً لمواصفات 2026-07-28 المقرة، بما في ذلك دورة input_required
  • لماذا يثبت كود التحكم في القدرات (capability-gating) الخاص بـ @modelcontextprotocol/sdk — وليس فقط وثائقه — أنه لا يعرف شيئاً عن tasks/update
  • كيف تقارن حزم SDK v2 المقسمة (@modelcontextprotocol/core و /server و /client)، وأين تقع @modelcontextprotocol/ext-tasks كالحزمة الوحيدة المتوافقة مع المواصفات المقرة والموجودة حالياً
  • كيفية تنفيذ طرق المهام الثلاث يدوياً باستخدام كلاس Server منخفض المستوى، بما في ذلك إعلان قدرة tasks التي يفرضها الـ SDK في وقت التشغيل
  • خطأ حقيقي لم يتم اكتشافه إلا بتشغيل الكود: قيمة غير قابلة للتحويل إلى سلسلة نصية (non-serializable) في سجل المهمة تؤدي إلى تعليق العميل دون ظهور خطأ في أي من الجانبين
  • كيف تبدو دورة حياة المهمة الكاملة من البداية إلى النهاية، من حالة working مروراً بـ input_required وصولاً إلى completed، مع مخرجات حقيقية مسجلة

ما تحدده إضافة Tasks فعلياً

تعتبر المهام (Tasks) امتداداً لبروتوكول Model Context Protocol، وليست تغييراً في البروتوكول الأساسي — يتم التفاوض على الدعم لكل طلب على حدة، وقد يعيد الخادم مقبض مهمة (task handle) بدلاً من النتيجة العادية لأي نوع طلب يغطيه الامتداد (حالياً فقط tools/call)1. عندما يقرر الخادم تأجيل استدعاء ما، فإنه يعيد CreateTaskResult — وهي نتيجة JSON-RPC تبدو عادية مع resultType: "task" وكائن Task مدمج بداخلها:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "task",
    "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840",
    "status": "working",
    "createdAt": "2025-11-25T10:30:00Z",
    "lastUpdatedAt": "2025-11-25T10:40:00Z",
    "ttlMs": 60000,
    "pollIntervalMs": 5000
  }
}

من هنا، يمتلك العميل ثلاث عمليات، ثلاث فقط1:

الطريقة (Method)الغرضقاعدة ملحوظة
tasks/getالتحقق من الحالة الحالية؛ الحالات النهائية تدرج الـ result أو الـ error الخاصة بها مباشرة في الاستجابةلا يوجد استدعاء منفصل لـ "جلب النتيجة" — استدعاء tasks/get لمهمة بحالة completed يعيد النتيجة نفسها
tasks/updateالإجابة على حالة input_required للمهمة عبر inputResponses، المرتبطة بـ inputRequests التي أرسلها الخادمالتأكيد يكون بنظام "أرسل وانسَ" (fire-and-forget)؛ يستأنف العميل التحقق عبر tasks/get لرؤية التأثير
tasks/cancelإرسال إشارة بالرغبة في إيقاف المهمةتعاوني فقط — قد ينهي الخادم المهمة على أي حال، والعميل غير ملزم بانتظار حالة cancelled قبل التخلص من الحالة المحلية

تنتقل الـ Task عبر خمس حالات ممكنة: working، input_required، completed، cancelled، failed1. المواصفات صريحة في أنه لا يوجد تعمد لوجود tasks/list:

"بسبب عدم وجود tasks/list، لا يمكن للخادم أن يسرب عن غير قصد وجود مهام خاصة بمستدعي إلى مستدعي آخر. هذا تحسين عن مواصفات المهام بتاريخ 2025-11-25، حيث كان من الممكن لقائمة ذات نطاق ضعيف أن تكشف عن معرفات مهام غير مرتبطة."1

هذا السطر مهم لما سيلي، لأن كود الـ SDK الذي يتم شحنه حالياً لا يزال يحتوي على طريقة tasks/list.

ما الذي يتم شحنه فعلياً: ثلاثة تنفيذات، مواصفة واحدة

قبل كتابة سطر واحد من كود الخادم، من المفيد معرفة ما يمكن تثبيته فعلياً اليوم (تم التحقق في 2026-09-18). هناك ثلاثة أماكن منفصلة يتواجد فيها منطق المهام (Tasks) في منظومة TypeScript، وهي لا تتفق مع بعضها البعض.

@modelcontextprotocol/sdk@1.30.0 — خط الـ SDK v1.x المستمر — يشحن وحدة ./experimental/tasks. تثبيتها وقراءة ملف dist/esm/types.js المجمع مباشرة يظهر سلاسل طرق JSON-RPC الحرفية التي يسجلها: tasks/get، tasks/result، tasks/list، tasks/cancel، وطريقة إشعارات notifications/tasks/status3. لا يوجد tasks/update في أي مكان في الحزمة. هذا هو شكل مسودة 2025-11-25 الذي يصفه قسم الأمان في المواصفات بأنه تم استبداله، وليس النسخة المعتمدة.

@modelcontextprotocol/{core,server,client}@2.0.0 — خط الإصدار v2 الذي تم تقسيمه حديثاً — يروي قصة أكثر وضوحاً مما يوحي به البحث السطحي عن سلاسل الطرق (method strings). المخرجات المجمعة (compiled output) منظمة في سجلات "عصر السلك" (wire era) منفصلة ومؤرخة: منطقة قديمة wire/rev2025-11-25/registry.ts، ومنطقة نشطة wire/rev2026-07-28/registry.ts وهي التي يرجع إليها إرسال الطلبات المباشر في الـ SDK فعلياً. لا تزال tasks/get و tasks/list و tasks/result و tasks/cancel و notifications/tasks/status موجودة في المخرجات المجمعة4 — ولكن فقط داخل السجل القديم، ويقرأ الـ JSDoc الموجود فوق كل منها مباشرة "@deprecated 2025-11-25 wire vocabulary with no SDK runtime; kept importable for interoperability only." أما السجل النشط بتاريخ 2026-07-28 — وهو السجل الذي تدعم فيه requestMethodKeys/notificationMethodKeys عملية إرسال الـ SDK الذي يعمل حالياً — فلا يحتوي على أي مدخلات لـ tasks/* أو notifications/tasks*، سواء بالشكل القديم أو الجديد4. لذا، لم يفشل v2 فقط في اللحاق بالشكل المعتمد المكون من ثلاث طرق؛ بل أسقطت مفردات السلك المباشرة لديه الـ Tasks تماماً، ولم يتبقَ الشكل القديم إلا كأنواع قديمة خاملة لا يراها أي معالج طلبات (request handler). وتؤكد مشكلة GitHub تم تقديمها بناءً على طلب أحد مطوري MCP بينما كان v2 لا يزال في المرحلة التجريبية (beta) أن هذا لم يكن حادثاً: "قام v2 بإزالة المهام التجريبية وفقاً لـ SEP-2663،" بانتظار إعادة تنفيذ صحيحة بمجرد أن تلحق حزمة الإضافة نفسها بالركب6. وحتى وقت كتابة هذه السطور، أصبح v2.0.0 متاحاً للعموم (GA) ولا السجل النشط ولا السجل القديم يطبقان الشكل المعتمد.

@modelcontextprotocol/ext-tasks@0.1.0 هي الحزمة الوحيدة المنشورة رسمياً من منظمة modelcontextprotocol والتي تتطابق مع ذلك. إنها التنفيذ المرجعي الرسمي للإضافة المعتمدة، والمنشورة من مستودع GitHub الخاص بـ modelcontextprotocol/ext-tasks5. يحتوي ملف المخطط (schema) المجمع لوحدة core/v2 الخاصة بها على مجموعة الطرق المعتمدة بالضبط — tasks/get و tasks/update و tasks/cancel و notifications/tasks — وهو ما تم تأكيده من خلال البحث في الـ JavaScript المبني، وليس مجرد قراءة سورس TypeScript5. لكن الحزمة مخصصة لجانب الطالب (requester-side) فقط: فنقطة دخول الـ client الخاصة بها (createTaskSessionFromClient و withTasks) تدير استدعاء أداة معزز بالمهام من العميل، وتتطلب صراحةً مثيلاً من Client الخاص بـ @modelcontextprotocol/client v2، وليس الخاص بـ SDK v1.x5. لا يوجد مساعد مكافئ منشور لجانب الخادم (server-side). أما وحدة الـ receiver المنفصلة فترتبط بنفس نوع Client v2 مثل جانب الطالب، ولكن موثق أنها تتعامل مع طلبات Tasks بتاريخ 2025-11-25 — وهو الشكل القديم مرة أخرى، ولسيناريو مختلف (أخذ عينات واستنتاج من الخادم إلى العميل، وليس tools/call).

بالجمع بين هذه النقاط: إذا كنت تريد خادماً يتحدث بلغة إضافة Tasks المعتمدة المكونة من ثلاث طرق اليوم، فلا توجد حزمة منشورة توفر لك ذلك. يجب عليك تنفيذها مقابل بدائيات البروتوكول منخفضة المستوى، وهو ما سيفعله بقية هذا المنشور.

بناء الخادم يدوياً

يستخدم خادم العرض أدناه كلاس Server منخفض المستوى الخاص بـ @modelcontextprotocol/sdk@1.30.0 — حيث تشير تعريفات الأنواع الخاصة بـ SDK إلى أنه @deprecated مع ملاحظة "استخدم McpServer بدلاً من ذلك من أجل API عالي المستوى. استخدم Server فقط لحالات الاستخدام المتقدمة"، وهو ما ينطبق على تنفيذ إضافة غير مدعومة بعد3. تم كتابة مخططات السلك (wire schemas) من الصفر بناءً على نص المواصفات المقتبس أعلاه، ولم يتم استيرادها من experimental/tasks، لأن تلك الوحدة تستخدم طرقاً خاطئة.

// schemas.ts — Zod schemas transcribed directly from the ratified spec.
import { z } from "zod";

export const TaskStatusSchema = z.enum([
  "working", "input_required", "completed", "cancelled", "failed",
]);

export const TaskSchema = z.object({
  taskId: z.string(),
  status: TaskStatusSchema,
  statusMessage: z.string().optional(),
  createdAt: z.string(),
  lastUpdatedAt: z.string(),
  ttlMs: z.number().nullable(),
  pollIntervalMs: z.number().optional(),
});

export const CreateTaskResultSchema = TaskSchema.extend({
  resultType: z.literal("task"),
});

export const GetTaskRequestSchema = z.object({
  method: z.literal("tasks/get"),
  params: z.object({ taskId: z.string() }),
});

export const UpdateTaskRequestSchema = z.object({
  method: z.literal("tasks/update"),
  params: z.object({
    taskId: z.string(),
    inputResponses: z.record(z.string(), z.object({
      action: z.string(),
      content: z.record(z.string(), z.unknown()).optional(),
    })),
  }),
});

export const CancelTaskRequestSchema = z.object({
  method: z.literal("tasks/cancel"),
  params: z.object({ taskId: z.string() }),
});

(يحدد الملف الكامل أيضاً مخططات مطابقة وهي GetTaskResultSchema و UpdateTaskResultSchema و CancelTaskResultSchema لاستجابات الطرق الثلاث، متبعة نفس النمط — تم اختصارها هنا لتوفير المساحة.)

تظهر أول مفاجأة في وقت التشغيل فوراً عندما تحاول تسجيل معالج (handler) لـ tasks/get. يقوم تابع setRequestHandler الخاص بـ Server منخفض المستوى باستدعاء فحص داخلي assertRequestHandlerCapability(method) قبل قبول التسجيل، ومصدره المترجم يحدد بدقة الطرق التي يتعرف عليها هذا الفحص:

// from @modelcontextprotocol/sdk@1.30.0's compiled server/index.js
case 'tasks/get':
case 'tasks/list':
case 'tasks/result':
case 'tasks/cancel':
    if (!this._capabilities.tasks) {
        throw new Error(`Server does not support tasks capability (required for ${method})`);
    }
    break;

ينتج عن جملة switch هذه أمران. أولاً، يجب عليك التصريح عن capabilities: { tools: {}, tasks: {} } عند إنشاء Server، وإلا فإن تسجيل معالج tasks/get أو tasks/cancel سيؤدي إلى خطأ فوراً. ثانياً، tasks/update ليست موجودة في تلك القائمة على الإطلاق — فبوابة القدرات (capability gate) في SDK ليس لديها أي مفهوم عنها، لذا فإن تسجيل معالج لها لا يتطلب ولا يستفيد من علامة قدرة tasks. هذه الفجوة في حد ذاتها هي دليل عملي على أن بنية قدرات المهام في SDK المتاحة تم بناؤها قبل شكل tasks/update.

// server.ts — advertise tools + tasks, register the standard tool methods,
// then register the three Tasks methods by hand.
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { GetTaskRequestSchema, UpdateTaskRequestSchema, CancelTaskRequestSchema } from "./schemas.js";

const server = new Server(
  { name: "tasks-demo-server", version: "0.1.0" },
  { capabilities: { tools: {}, tasks: {} } },
);

الأداة نفسها، summarize_report، تحول دائماً إلى مهمة (task) عندما يصرح المستدعي بدعم الإضافة، وتعود إلى نتيجة متزامنة عادية خلاف ذلك — حيث تنص المواصفات صراحة على أن "الخادم يجب ألا يعيد CreateTaskResult إلى عميل لم يدرج قدرة الإضافة في طلبه"1:

function clientDeclaredTasksCapability(meta: unknown): boolean {
  if (!meta || typeof meta !== "object") return false;
  const caps = (meta as Record<string, unknown>)["io.modelcontextprotocol/clientCapabilities"];
  const extensions = caps && typeof caps === "object" ? (caps as any).extensions : undefined;
  return !!extensions && "io.modelcontextprotocol/tasks" in extensions;
}

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const wantsTasks = clientDeclaredTasksCapability((request.params as any)._meta);
  if (!wantsTasks) {
    return { content: [{ type: "text", text: "Summary: (synchronous fallback)" }] };
  }

  const taskId = crypto.randomUUID();
  const created = new Date().toISOString();
  const record = {
    taskId, status: "working" as const,
    createdAt: created, lastUpdatedAt: created,
    ttlMs: 5 * 60 * 1000, pollIntervalMs: 500,
  };
  tasks.set(taskId, record);
  scheduleWork(taskId, (request.params.arguments as any)?.simulateFail); // simulated background work; see below for a bug it exposed
  return { resultType: "task", ...record };
});

العميل، وحلقة الاستطلاع (poll loop)

يصرح العميل عن الإضافة بنفس الطريقة، في _meta ضمن طلب tools/call، ثم يدير حلقة الاستطلاع الخاصة به ضد tasks/get — مع الالتزام بـ pollIntervalMs وتغيير السلوك بناءً على status:

const initial = await client.request(
  {
    method: "tools/call",
    params: {
      name: "summarize_report",
      arguments: { reportId },
      _meta: {
        "io.modelcontextprotocol/clientCapabilities": {
          extensions: { "io.modelcontextprotocol/tasks": {} },
        },
      },
    },
  },
  CallToolOrTaskResultSchema,
);

if (initial.resultType !== "task") { /* handle the synchronous path */ }

let { taskId, pollIntervalMs = 1000 } = initial;
while (true) {
  await sleep(pollIntervalMs);
  const status = await client.request(
    { method: "tasks/get", params: { taskId } },
    GetTaskResultSchema,
  );
  pollIntervalMs = status.pollIntervalMs ?? pollIntervalMs;

  if (status.status === "input_required") {
    await client.request(
      {
        method: "tasks/update",
        params: {
          taskId,
          inputResponses: {
            redaction_level: { action: "accept", content: { level: "partial" } },
          },
        },
      },
      UpdateTaskResultSchema,
    );
    continue;
  }
  if (status.status === "completed") { console.log(status.result); break; }
  if (status.status === "failed") { console.log(status.error); break; }
  if (status.status === "cancelled") break;
}

دورة الـ input_required

تتوقف معظم التغطية الحالية للمهام عند الحالة البسيطة — إنشاء مهمة، استطلاعها، والحصول على نتيجة. الجزء الأكثر فائدة في المواصفات، والجزء الذي يميز المهام فعلياً عن نمط "الاستطلاع حتى الانتهاء" العادي، هو input_required: حيث يمكن للخادم إيقاف مهمة مؤقتاً في منتصف التنفيذ، وإظهار طلب واحد أو أكثر معلقين في inputRequests، وانتظار العميل للإجابة عليها من خلال tasks/update قبل الاستئناف1. يطبق خادم العرض هذا الأمر عمداً: يتوقف العمل المحاكى لـ summarize_report بعد حوالي 1.5 ثانية للسؤال عن مستوى تنقيح معلومات الهوية الشخصية (PII) المراد تطبيقه، باستخدام نفس شكل elicitation/create الذي قد يستخدمه طلب استيضاح مباشر:

{
  "status": "input_required",
  "inputRequests": {
    "redaction_level": {
      "method": "elicitation/create",
      "params": {
        "mode": "form",
        "message": "Report Q3-vendor-audit: how should PII be redacted in the summary?",
        "requestedSchema": {
          "type": "object",
          "properties": { "level": { "type": "string", "enum": ["none", "partial", "full"] } },
          "required": ["level"]
        }
      }
    }
  }
}

يجيب العميل بمفتاح مطابق في inputResponses عند استدعاء tasks/update. يكون الإقرار (acknowledgement) فارغاً عن قصد ومتسقاً في النهاية — حيث تسمح المواصفات للخادم بقبول الاستجابة والإقرار بها قبل أن تعكس حالة الاستطلاع للمهمة ذلك — لذا فإن التحرك الصحيح الوحيد للعميل هو الاستمرار في استطلاع tasks/get، وليس معاملة استجابة tasks/update نفسها كتأكيد على حدوث أي تغيير1.

المخرجات الفعلية

تشغيل العميل مقابل الخادم من البداية للنهاية، بدون أي محاكاة (mocking)، ينتج عن ذلك هذا (مستوى التعتيم partial، ثم استدعاء ثانٍ يختبر عمدًا مسار failed):

--- calling summarize_report(reportId=Q3-vendor-audit, simulateFail=false) ---
Task created: e08594eb-1dd4-4066-b120-d35b442513db (status=working, pollIntervalMs=500)
  poll 1: status=working
  poll 2: status=working
  poll 3: status=input_required
  server needs input: {
  mode: 'form',
  message: 'Report Q3-vendor-audit: how should PII be redacted in the summary?',
  requestedSchema: { type: 'object', properties: { level: [Object] }, required: [ 'level' ] }
}
  sent tasks/update with redaction level = partial
  poll 4: status=working
  poll 5: status=completed
  completed. result: {
  "content": [
    { "type": "text", "text": "Summary ready (redaction=partial): report is 3 pages, 2 action items, 1 blocked dependency." }
  ],
  "isError": false
}

--- calling summarize_report(reportId=Q3-vendor-audit-2, simulateFail=true) ---
Task created: 41652027-6ec0-43cb-b48b-074319c2bb1c (status=working, pollIntervalMs=500)
  poll 1: status=working
  poll 2: status=working
  poll 3: status=failed
  failed: { code: -32603, message: 'Upstream summarizer timed out' }

كما أكد تشغيل منفصل حالتي الحافة اللتين ذكرتهما المواصفات صراحةً: العميل الذي يتجاهل إعلان القدرات (capability declaration) يحصل على نتيجة التراجع المتزامنة العادية، ولا يحصل أبدًا على CreateTaskResult1، وإرسال tasks/cancel ضد مهمة working ينقلها إلى حالة cancelled في عملية الاستطلاع (poll) التالية، وهو ما يتوافق مع كون الإلغاء تعاونيًا وليس مضمونًا1.

الخطأ الذي سيتسبب في تجميد العميل بصمت

كان الإصدار الأول من معالج الأدوات في الخادم يخزن مقبض setTimeout الخاص بكل مهمة مباشرة على نفس الكائن الذي يعيده إلى العميل، وذلك لتسهيل عملية التنظيف عند الإلغاء:

const record = { taskId, status: "working", /* ...*/ };
tasks.set(taskId, record);
record._timer = setTimeout(() => { /* advance the task */ }, 1500);
return { resultType: "task", ...record }; // _timer included by the spread

تشغيل العميل مقابل هذا الإصدار لم يتسبب في ظهور خطأ في أي من الطرفين — بل تجمد ببساطة إلى الأبد عند أول استدعاء لـ tools/call. عمل معالج الخادم وعاد بنجاح؛ لكن العميل لم يتلقَّ استجابة أبدًا. السبب: كائن NodeJS.Timeout يحتوي على مراجع دائرية داخلية، لذا فإن تسلسله (serializing) كجزء من نتيجة JSON-RPC يفشل داخل مسار الكتابة في وسيلة النقل (transport)، وهذا الفشل لا يظهر مرة أخرى للطلب الذي تسبب فيه. الحل هو تفكيك الحقل غير القابل للتسلسل قبل الإعادة:

const { _timer, ...publicRecord } = record;
return { resultType: "task", ...publicRecord };

يتم الكشف عن هذا هنا لأنه بالضبط نوع نمط الفشل الذي يكون غير مرئي عند قراءة التوثيق ولا يظهر إلا من خلال تشغيل كود مدعوم بالمهام فعليًا مقابل عميل حقيقي — وهو ما يبدو أن جميع المقالات الحالية التي تقارن بين SDK والمواصفات لم تفعله.

ثلاثة تنفيذات، جنبًا إلى جنب

sdk v1.30.0 experimental/taskscore/server/client v2.0.0 (مدمج)ext-tasks v0.1.0 core/v2
يطابق مواصفات 2026-07-28 المعتمدةلا — شكل 2025-11-25لا — سجل السلك النشط لا يحتوي على طرق Tasks على الإطلاق4نعم5
الطرق المتوفرةtasks/get, tasks/list, tasks/result, tasks/cancel3لا يوجد نشط — نفس الطرق الأربعة موجودة فقط كأنواع قديمة مهجورة وغير تشغيلية4tasks/get, tasks/update, tasks/cancel5
دعم tasks/update / input_requiredلالانعم، من جانب العميل
نشر مساعد من جانب الخادمنعم (شكل خاطئ)لا — توجد أنواع مساعدة قديمة ولكنها لا تحمل وقت تشغيل SDK4لا — من جانب الطالب فقط
قابل للاستخدام اليوم لخادم يتبع المواصفات المعتمدةلالالا (العميل فقط)

التحقق

تم التأكد من كل رقم إصدار، واسم ميثود (method)، وسلوك تقييد القدرات (capability-gating) في هذا المنشور من خلال تثبيت الحزم الفعلية وقراءة مخرجاتها المجمعة (compiled output)، وليس من خلال قراءة التوثيق وحده. تم تحديد @modelcontextprotocol/sdk@1.30.0 على أنه علامة latest الحالية لـ npm وقت الكتابة؛ وتم عمل grep مباشرة لملفات dist/esm/types.js و dist/esm/server/index.js للبحث عن نصوص ميثودز JSON-RPC وجملة switch الخاصة بتقييد القدرات المقتبسة أعلاه. كما تم تثبيت @modelcontextprotocol/core@2.0.0 و /server@2.0.0 و /client@2.0.0 بنفس الطريقة وعمل grep لنفس النصوص، مما أدى إلى قراءة الكود المجمع المحيط بها مباشرة — وهذا ما كشف عن وجود سجلين منفصلين من عصر الـ wire ووجود JSDoc الخاص بـ @deprecated ... no SDK runtime على السجل القديم. كما تم تثبيت @modelcontextprotocol/ext-tasks@0.1.0 وقراءة ملفات core/v2 و client و receiver من نوع .d.ts والملفات المجمعة .js مباشرة للتأكد من الميثودز ونوع العميل الذي تستهدفه كل نقطة دخول. كما تمت قراءة قضية GitHub المتعلقة بإزالة المهام في نسخة beta v2 بالكامل، ولم يتم تلخيصها من مصدر ثانوي.

كود السيرفر والعميل الموضح أعلاه تمت كتابته في مشروع حقيقي (Node.js v22.23.2, TypeScript 7.0.2, tsx 4.23.13, Zod 4.6.5)، وتم فحص الأنواع باستخدام tsc --strict --noEmit، وتم تنفيذه من البداية للنهاية عبر ناقل stdio حقيقي بين عمليتين منفصلتين — وليس بشكل وهمي (mocked). كل انتقال حالة موضح في قسم "المخرجات الحقيقية"، بما في ذلك دورة input_required/tasks/update، والحالات النهائية completed و failed، ومسار التراجع المتزامن (synchronous-fallback)، والإلغاء، تم اختبارها عن طريق تشغيل الكود والتقاط مخرجات الكونسول الفعلية، والتي تمت إعادة إنتاجها أعلاه مع تعديل المسافات البيضاء فقط لتناسب عرض السطر. تم اكتشاف خطأ تسلسل (serialization bug) الـ _timer بهذه الطريقة، ولم يتم اختلاقه من أجل المنشور.

الخلاصة

تعتمد إضافة MCP Tasks المعتمدة تصميماً نظيفاً يتكون من ثلاث طرق — tasks/get، و tasks/update، و tasks/cancel — مع تدفق مدخلات مفيد وحقيقي أثناء تنفيذ المهمة. ما ليس صحيحاً بعد، حتى سبتمبر 2026، هو أن تثبيت SDK الرسمي يمنحك تنفيذاً لها: فإصدار v1.x المستقر لا يزال يستخدم شكل الإضافة السابق قبل الاعتماد، وحزم v2 الجديدة لا تدعم أي إصدار منها في مفردات الربط النشطة على الإطلاق، والحزمة الوحيدة المنشورة رسمياً والتي تتوافق مع المواصفات المعتمدة تغطي جانب العميل فقط. بناء خادم متوافق اليوم يعني تجاوز مساعدات experimental/tasks في SDK وتنفيذ الطرق الثلاث مباشرة وفقاً لتنسيق الربط الموثق — وهو أمر يتطلب بضع عشرات من أسطر الكود، وليس إطار عمل كاملاً، بمجرد أن تدرك أن جملة switch الخاصة ببوابة القدرات لا تتعرف إلا على أسماء الطرق القديمة.

Footnotes

  1. Model Context Protocol, "Tasks" (MCP Tasks Extension, spec version 2026-07-28) — https://tasks.extensions.modelcontextprotocol.io/specification/draft/tasks (three methods, Task status enum, capability negotiation, input_required/inputRequests/inputResponses flow, security considerations including the removal of tasks/list, cancellation semantics; fetched 2026-09-18) 2 3 4 5 6 7 8 9 10 11 12 13 14

  2. Model Context Protocol, "Specification" (2026-07-28) — https://modelcontextprotocol.io/specification/2026-07-28 (lists Tasks as an official extension of the 2026-07-28 spec release; fetched 2026-09-18)

  3. @modelcontextprotocol/sdk v1.30.0 on npm — https://registry.npmjs.org/@modelcontextprotocol/sdk/latest (resolved as latest; experimental/tasks export path, method literals tasks/get/tasks/result/tasks/list/tasks/cancel/notifications/tasks/status confirmed in compiled dist/esm/types.js; capability-gating switch statement confirmed in dist/esm/server/index.js; Server class documented @deprecated; installed and inspected directly, 2026-09-18) 2 3 4 5

  4. @modelcontextprotocol/core, @modelcontextprotocol/server, @modelcontextprotocol/client, all v2.0.0 on npm (installed and inspected directly, 2026-09-18) — compiled dist output contains two versioned wire-era registries; the wire/rev2025-11-25/registry.ts region defines tasks/get, tasks/cancel, tasks/list, tasks/result, and notifications/tasks/status, each documented @deprecated 2025-11-25 wire vocabulary with no SDK runtime; kept importable for interoperability only; the active wire/rev2026-07-28/registry.ts region's requestMethodKeys/notificationMethodKeys — which the SDK's live dispatch consults — contain no tasks/* or notifications/tasks* entries at all; tasks/update is absent from both registries 2 3 4 5 6 7

  5. @modelcontextprotocol/ext-tasks v0.1.0 on npm, reference implementation from https://github.com/modelcontextprotocol/ext-tasks (based on SEP-2663; Apache-2.0; installed and inspected directly, 2026-09-18) — core/v2/schemas.js method literals tasks/get/tasks/update/tasks/cancel/notifications/tasks confirmed matching the ratified spec; client entry point requires @modelcontextprotocol/client (v2); receiver entry point documented as handling 2025-11-25-era sampling/elicitation task requests 2 3 4 5 6 7

  6. GitHub, modelcontextprotocol/modelcontextprotocol issue #3051, "docs: update or freeze SEP-1686 tasks code examples for the v2 SDK" — https://github.com/modelcontextprotocol/modelcontextprotocol/issues/3051 (filed 2026-07-08 at maintainer David Soria Parra's request; states TypeScript SDK v2 was then at 2.0.0-beta.2 as split @modelcontextprotocol/{client,server,core,node,express,hono,server-legacy} packages and that "v2 removed experimental tasks per SEP-2663"; fetched 2026-09-18)

الأسئلة الشائعة

tasks/get (لفحص الحالة، مع إدراج النتيجة أو الخطأ بمجرد الوصول للحالة النهائية)، و tasks/update (للرد على طلب input_required )، و tasks/cancel (لطلب الإلغاء). لا توجد ميثود tasks/list عن قصد 1 .