Claude Media
MCP Tasksが拡張機能に移った理由 — tasks/resultはなぜ廃止されたか

MCP Tasksが拡張機能に移った理由 — tasks/resultはなぜ廃止されたか

実験的機能だったMCP Tasksは2026-07-28リビジョンでコア仕様から公式拡張機能へ移りました。ブロッキング型のtasks/resultが廃止された背景と、ポーリング型の新しい設計を読み解きます。

MCP TasksはCI実行やバッチ処理のような、すぐには終わらない処理を扱うための仕組みです。この機能は当初、実験的な状態のままコア仕様の一部に置かれていました。2026-07-28リビジョンで、io.modelcontextprotocol/tasksという公式拡張機能(提案文書SEP-2663)へ切り出されています。単なる置き場所の変更ではなく、ブロッキング型だったメソッドを廃止し、ポーリング中心の設計に作り直す再設計です。

MCP Tasksとは何をするための機能か

MCP Tasksとは、時間のかかる処理に対してサーバーが即座の結果の代わりに「持続的なハンドル(タスクID)」を返す仕組みです。クライアントはそのIDを使って進捗をポーリングしたり、必要なタイミングで入力を渡したり、再接続後に最終結果を取得したりできます。CIパイプラインやバッチ処理、人間の承認待ちのように、数秒から数時間かかる処理が対象になります。

接続を張ったままブロックする方式では対応できない問題をTasksは解決します。多くのクライアントやトランスポート中継はタイムアウトを課すため、長時間の接続維持は現実的ではありません。タスクIDは持続的なハンドルなので、クライアントが切断や再起動をしても同じIDでポーリングを再開できます。ステータスはworking・input_required・completed・failed・cancelledの5種類で表現されます。途中で入力が必要になった場合はinput_requiredに遷移し、クライアントはtasks/updateで応答します。

手順

1つのタスクが完了するまで

  1. 1

    サーバーがタスクを作って返す

    tools/callなどへの応答として、resultType: "task"のCreateTaskResultを返します。中身はtaskId・初期ステータス・ttlMs・pollIntervalMsです。タスクは応答を送る前に、永続的に作成済みでなければなりません。pollIntervalMsは省略されることがあり、ttlMsはnull(無期限)の場合もあります。作成・更新時刻(createdAt・lastUpdatedAt)も必ず含まれます。

  2. 2

    クライアントがtasks/getでポーリングする

    pollIntervalMsを守りながら、終端ステータスに着くまで繰り返します。終端ではcompletedならresult、failedならerrorが返ります。

  3. 3

    入力が要るときはtasks/updateで答える

    ステータスがinput_requiredになると、tasks/getの応答にinputRequestsのマップが載ります。クライアントは対応するinputResponsesをtasks/updateで送ります。

  4. 4

    結果を受け取る、または止める

    completedでresultを受け取ります。途中で中止したいときはtasks/cancelを送りますが、効くとは限りません。

tasks/updateの受け手の作法も決まっています。サーバーは空の結果で応答し、未知のキーや応答済みのキーに対するinputResponsesは無視します。クライアントが再送しても壊れにくい作りです。

実験的だったTasksが抱えていた3つの実装課題

コア仕様に残したままでは解決できない課題が、実装が進むにつれて明らかになりました。SEP-2663の提案文書は、実装を進めるなかで浮かび上がった3つの実装課題を挙げ、そのうえで、それらとは別枠の構造的な問題を1つ指摘しています。

1つ目の実装課題はハンドシェイクの脆さです。旧設計では2つの仕組みが並存していました。1つはメソッドレベルのcapability(機能対応の宣言)で、tasks.requests.tools.callが「tools/callはタスク拡張の対象になり得る」ことを宣言します。もう1つはツールレベルのexecution.taskSupportフィールドで、個別のツールがタスク拡張を受け入れるかどうかを示します。クライアントはtools/listを事前に呼んでおかないと、どのツールにタスクパラメータを付けてよいか判断できません。すべてのツールに機械的にタスクパラメータを付けることもできない、という分かりにくい設計でした。

2つ目の実装課題はtasks/resultが「ブロッキングの罠」だったことです。input_requiredを観測したクライアントはtasks/resultを早い段階で呼ぶ必要がありました。サーバー側がSSEストリームを開いてElicitationやSamplingのリクエストをサイドチャネルで送れるようにするためです。ところがtasks/resultは処理全体が終わるまでブロックします。長時間持続する接続を要求するもので、実装したくないクライアント・サーバーが多いうえ、サーバーからクライアントへの一方的なリクエストを禁じるSEP-2260の方針とも矛盾していました。

3つ目の実装課題はtasks/listのスコープが定義できなかったことです。他人のタスクを勝手にキャンセルしたり結果を取得したりできないようにするには、タスクを何らかの「認可コンテキスト」に紐づける必要があります。しかしその実装はサーバーごとの独自の権限モデルに委ねられていました。この紐づけができないサーバーでは、タスクIDそのものが唯一の防御線になってしまい、tasks/listを提供すること自体が安全でなくなります。セッション単位で紐づける案もありましたが、SEP-2567がプロトコルからセッションの概念そのものを取り除いたため、この逃げ道もふさがれました。

3つの実装課題とは別に、SEP-2663はもう1つ構造的な問題を挙げています。クライアント側がElicitationやSamplingのタスクをホストする従来の方式が、成立しなくなっていたことです。この方式は、サーバーからクライアントへの一方的なリクエストを禁じるSEP-2260の方針のもとでは「クライアントへの一方的なリクエスト」に該当してしまい、そのままでは維持できません。実装上の不具合というより、Tasksの設計そのものが前提としていた通信の向きが、別のSEPによって塞がれたという構造的な矛盾です。この方式はもともとSEP-1686が、タスクをツール呼び出しに結合させないために認めたものでした。

コア仕様から拡張機能への移動で何が変わったか

くらべる

2025-11-25の実験的Tasksと拡張機能版の違い

実験的

旧設計(コア仕様内)

メソッドレベルのcapabilityとツールのexecution.taskSupportの二重の宣言が必要で、クライアントは先にtools/listを呼ぶ必要がありました。input_requiredではtasks/resultでSSEを開き、処理の完了までブロックします。tasks/listもありました。

2026-07-28

新設計(拡張機能)

宣言は拡張機能capabilityの1か所です。ポーリングはtasks/getと新設のtasks/updateに一本化され、tasks/resultとtasks/listは廃止されました。サーバーはタスクフラグのないリクエストにも、自分の判断でタスクを返せます。

// クライアントがper-requestのcapabilitiesでタスク拡張への対応を宣言する
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/clientCapabilities": {
        "extensions": { "io.modelcontextprotocol/tasks": {} }
      }
    }
  }
}

サーバー側もserver/discover(サーバーの対応機能を問い合わせるメソッド)が返すcapabilities(対応機能の一覧)で同じ拡張機能を宣言し、対応していないクライアントにタスクを返すことはありません。

ステータス意味
working意味処理が進行中
input_required意味サーバーが入力を必要としている。inputRequestsを参照
completed意味処理が完了。resultに最終結果
failed意味JSON-RPCエラーが発生。errorに詳細
cancelled意味キャンセルされた(必ず反映されるとは限らない)

completed・failed・cancelledは終端状態で、一度到達すると変化しません。サーバーはnotifications/tasksでステータス変更をプッシュ通知することもできます。対応しているクライアントはsubscriptions/listenという購読の仕組みを通じて、これをポーリングの代わりに使えます。このsubscriptions/listen自体も2026-07-28リビジョンの新機能です。旧来のHTTP GETエンドポイントとresources/subscribe/resources/unsubscribeを置き換えました。Tasksの通知経路も、MCP全体がステートレス化し、サーバーからの通知を1本の長時間POSTレスポンス・ストリームへ集約していく流れの上に乗っています。デフォルトはポーリングで、通知はサーバーが対応している場合の追加の選択肢という位置づけです。

Tasksが向いているユースケース

すべての長時間処理にTasksが必要なわけではありません。公式ドキュメントが挙げる代表的な適用場面は5つです。

ユースケース具体例
長時間処理具体例CIパイプライン、バッチデータ処理、モデル学習ジョブ
Human-in-the-loop具体例承認ゲート、レビューステップ、ユーザー確認待ちの処理
外部ジョブシステムの呼び出し具体例すでにジョブIDを使うAPI(クラウドデプロイ、非同期API、キュー処理)のラップ
不安定な接続具体例モバイルクライアント、断続的なネットワーク、接続が切れやすい環境
バッチ処理具体例大量データの一括インポート・更新など、部分的な進捗報告に意味がある処理

共通しているのは「呼び出し元が結果を待つ間、接続を張り続けたくない、あるいは張り続けられない」という制約です。逆に言えば、数百ミリ秒からせいぜい数秒で終わる処理にTasksを導入する理由はなく、通常のリクエスト/レスポンスのままで十分です。

クライアント実装で見落としやすいのが、タスクIDの永続化です。クライアントがクラッシュや再起動をしてもポーリングを再開できるよう、タスクIDは揮発性のメモリではなく永続的な保存先に書いておく必要があります。tasks/cancelによるキャンセルも、送ればすぐ止まるとは限りません。キャンセルはあくまで協調的な扱いで、サーバーは意図を確認したことを応答するだけで、実際には止められずにcancelled以外の終端ステータスへ到達することもあります。

拡張機能への移動は「格下げ」ではなく独立したリリースサイクルの獲得

コア仕様から拡張機能への移動は、機能としての後退ではなく独立したリリースサイクルの獲得です(こうした拡張機能化の流れを含め、次の半年で優先される5領域はMCPの2026年ロードマップで整理しています)。SEP-2663の提案は、拡張機能として再定義することで「コア仕様のリビジョンサイクルとは切り離して機能を育てる時間を確保し、採用を後押しする」ことを明示的な狙いとして挙げています。コア仕様に留まったままでは、Tasksの設計変更もコア仕様のリリース周期に縛られ、他の機能の変更と足並みをそろえる必要がありました。拡張機能になったことで、Tasks固有のスキーマはext-tasksリポジトリが日付付きで公開します。2026-07-28のスキーマは安定版(Stable)、開発中のものはdraftとして分かれています。リリース済みのスキーマは変更されないスナップショットだと、リポジトリには書かれています。コア仕様のリビジョンとは別の周期で更新できる構造です。

あゆみ

Tasksの置き場所の移り変わり

  1. 2025-11-25コア仕様に実験的機能として収録

    SEP-1686で導入された、ツール呼び出しやElicitation、Samplingを非同期にする方式です。この版のTasksは、拡張機能版の提案で削除対象になりました。

  2. 2026-07-28公式拡張機能として再定義

    io.modelcontextprotocol/tasksが、SEP-2663に基づく公式拡張機能になりました。スキーマも2026-07-28の版名で安定版(Stable)として公開されています。

  3. 将来安定後はコア仕様への昇格が想定されている

    SEP-2663は、拡張機能が安定して広く採用された段階でコアへ昇格させる意図を書いています。時期は示されていません。

この流れは、拡張機能の仕組みが整うまでの暫定として実験的Tasksがあったという位置づけと整合します。TypeScriptでは@modelcontextprotocol/ext-tasksパッケージが、クライアント側のライフサイクルAPIを提供しています。旧2025-11-25版Tasksの受け手側サポートも含むため、旧版との共存にも使えます。

対応状況がクライアントによって異なる点も押さえておく必要があります。TasksはコアのMCP仕様に対する拡張機能(extension)という位置づけであり、ホスト側の対応はクライアントごとにばらつきます。公式ドキュメントは、タスクの利用にはクライアントとサーバー双方の明示的なオプトインが必要だと書いています。実装者が確認すべきなのは、自分の使うクライアントがio.modelcontextprotocol/tasks拡張機能への対応をclientCapabilitiesで宣言しているか、そしてサーバー側もserver/discoverのcapabilitiesで同じ拡張機能を返しているかの2点です。「MCP対応クライアントなら使えるはず」と決め打ちすると、この食い違いに気づけません。

Tasksを使う実装者は何を確認すればよいか

旧設計の実装が残っているかどうかは、次の4点で見分けられます。

手順

旧設計からの移行チェック

  1. 1

    tasks/resultへの依存を探す

    完了待ちをtasks/resultのブロッキングに頼っていれば、tasks/getのポーリングへ置き換えます。

  2. 2

    入力待ちの処理をtasks/updateに寄せる

    input_requiredでのサイドチャネル送信をやめ、inputRequestsをtasks/getで返す形にします。クライアントはtasks/updateで答えます。

  3. 3

    tasks/listの利用箇所を移す

    タスク一覧は仕様から消えました。必要なら、呼び出し元アプリケーション側でタスクIDを管理します。

  4. 4

    ハンドシェイクを1か所にまとめる

    execution.taskSupportやtools/listの事前確認は不要です。拡張機能capabilityの宣言と確認だけを残します。

MCPサーバーを自作している場合、長時間処理の設計はサーバーの使い勝手を大きく左右します。Tasksの再設計は、実装方針を見直す機会になります。タスクIDが認可の唯一の防御線になり得るという課題は、MCPサーバーの権限設計を考えるときの論点とも重なります。MCPの基本的な仕組みそのものから確認したい場合はMCPとは何かを解説した記事が出発点になります。

この記事を共有:XはてブLinkedIn
MCP をもっと見る →