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でポーリングを再開できます。ステータスはworkinginput_requiredcompletedfailedcancelledの5種類で表現されます。途中で入力が必要になった場合はinput_requiredに遷移し、クライアントはtasks/updateで応答します。

実験的だった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によって塞がれたという構造的な矛盾です。

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

再設計後のTasksは、ポーリングのライフサイクルをtasks/getと新設のtasks/updateに一本化し、ブロッキング型のtasks/resultを廃止しました。サーバーがタスクを返せるかどうかの判断は、拡張機能capability(拡張機能への対応可否)という単一のハンドシェイクポイントに集約され、tools/listでの事前確認や個別のフラグは不要になりました。サーバーは通常のリクエスト(タスクフラグの付いていないリクエストを含む)に対しても、判断次第でタスクを返せます。

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

サーバー側もserver/discover(サーバーの対応機能を問い合わせるメソッド)が返すcapabilities(対応機能の一覧)で同じ拡張機能を宣言し、対応していないクライアントにタスクを返すことはありません。tasks/listは新しい設計から削除され、スコープが定義できないという課題そのものが取り除かれています。クライアント側がホストするElicitation/Samplingタスクも、SEP-2260への準拠のため廃止されました。この方式はもともとSEP-1686によって、タスクをツール呼び出しに結合させないための工夫として認められたものです。しかし、サーバーからクライアントへの一方的なリクエストを禁じる方針とは両立できなくなりました。

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

completedfailedcancelledは終端状態で、一度到達すると変化しません。サーバーは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以外の終端ステータスへ到達することもあります。

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

コア仕様から拡張機能への移動は、機能としての後退ではなく独立したリリースサイクルの獲得です。SEP-2663の提案は、拡張機能として再定義することで「コア仕様のリビジョンサイクルとは切り離して機能を育てる時間を確保し、採用を後押しする」ことを明示的な狙いとして挙げています。コア仕様に留まったままでは、Tasksの設計変更のたびに仕様全体の破壊的変更(breaking change)として扱われ、他の機能の変更と足並みをそろえる必要がありました。拡張機能になったことで、Tasks固有のスキーマはext-tasksリポジトリで独自にバージョニングされます。コア仕様のリビジョンとは非同期に更新できるようになりました。

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

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

長時間かかる処理をMCPサーバーとして公開する立場なら、まずtasks/resultを前提にした旧設計の実装が残っていないかを確認します。すでに動いているサーバーであれば、ポーリングをtasks/gettasks/updateに置き換えます。tasks/listに依存したタスク一覧機能があれば、別の手段(呼び出し元アプリケーション側での管理など)に移す必要があります。MCPサーバーを自作している場合、長時間処理をどう設計するかはサーバーの使い勝手を大きく左右する部分なので、Tasksの再設計は実装方針を見直す良いタイミングです。タスクIDが認可の唯一の防御線になり得るという課題は、MCPサーバーの権限設計を考えるときの論点とも重なります。MCPの基本的な仕組みそのものから確認したい場合はMCPとは何かを解説した記事が出発点になります。

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