数分かかるMCPツール、Claude Codeで待たずに進めるには? 自動バックグラウンド化で足りる場合と、MCP Tasksが要る場合
Claude Codeに数分かかるMCPツールをつなぐと、モデルはいつ別の作業に進めるのか。自動バックグラウンド化(既定2分)とMCP Tasks(サーバーが受付だけ先に返す)を、Claude Code 2.1.310と自作サーバーで14条件を実測して比べました。0秒・10秒・120秒の違い、条件を外すと通常呼び出しに戻る様子、入力を閉じると仕事が止まる様子、選び方の判断フロー、動くサンプルコードまでまとめています。
文・ニコ

Claude CodeにMCPツールをつないだら、1回の呼び出しに数分かかった。レポートの書き出し、ビルド、データの集計。こういうこと、ありますよね。
待っている間、Claudeは何もできないのか。それとも別の作業を進めてくれるのか。進めてくれるなら、いつから? 自分のツールでそうするには、何を直せばいい?
この疑問に答えるために、仕組みの説明だけでなく、Claude Code 2.1.310を実際に動かして、時刻を測りました。自作の小さなMCPサーバーを2種類用意して、14の条件で比べています。
結論から。
- Claudeが別の作業に進めるようになるのは、自動バックグラウンド化なら閾値の時間後、MCP Tasksなら受付の応答と同時でした。実測では、閾値10秒の設定で10.0秒後、既定の120秒設定で120.0秒後、Tasksは0.0秒後です。
- 仕事そのものは速くなりません。どの方式でも、40秒の仕事は40秒で終わりました。変わるのは「待たされる時間」です。
- サーバーを直せないなら、自動バックグラウンド化で足ります。 設定は要りません。閾値は環境変数で変えられます。
- Tasksが要るのは、受付した直後から進めたい、Claude Codeを閉じても続けたい、といった場合です。ただしサーバー側の実装が要ります。
- Tasksに対応したサーバーでも、呼び出しごとに条件が合わないと、ただの待ち呼び出しに戻ります。実測では、条件を外した3パターンすべてで40秒待たされました。
知りたいことごとに、読む場所を案内します。
- 「Claude Codeで時間のかかるMCPツールを使う」人:まず「2つの仕組み」と「選び方」を読めば足ります。
- 「自分でMCPサーバーを作る」人:「サーバーを作る人向け」まで読んでください。動くコードがあります。
- MCP Tasksの2025-11-25版から2026-07-28版への変更点が知りたい人:別の記事にまとめています。この記事では深入りしません。
2つの「裏で動く」を分けておく
Claude Codeには、長いMCPツール呼び出しを裏で動かす仕組みが2つあります。見た目は似ていますが、別物です。
| 自動バックグラウンド化 | MCP Tasks | |
|---|---|---|
| 誰がやる? | Claude Code | MCPサーバー |
| いつ裏に回る? | 普通の呼び出しが閾値(既定2分)を超えたとき | サーバーが「引き受けた」と返したとき |
| サーバーの対応 | 不要 | 必要 |
| 元の呼び出しは? | 終わるまで続く | 受付の応答で終わる |
自動バックグラウンド化はClaude Code側の機能です。メイン会話でのMCPツール呼び出しが2分たってもまだ終わらないと、Claude Codeがそれをバックグラウンドタスクに移します。Claudeは先にタスクIDを受け取り、作業を続けます。結果は仕事が終わったときに通知で届きます。
MCP Tasksはサーバー側の仕組みです。サーバーが「この仕事は長いから、引き受けて、あとで結果を取りに来てね」とIDを返します。クライアントはそのIDで状態を聞きに行きます(tasks/get)。MCPの公式拡張として、2026-07-28版の仕様に入っています。
2分で裏に回る、というのは自動バックグラウンド化の話です。Tasksに2分は関係ありません。
詳しい仕様の違いは前の記事で公式文書をもとに整理しました。ここからは、実際に動かしたらどうなったかの話です。
何をどう測ったか
Claude Codeの公式文書には条件がたくさん書かれています。でも、「実際に2分で裏に回るの?」「Tasksは本当に即座に返るの?」と、確かめたくなりますよね。書いてあるとおりに動くのか、測ってみました。
実験には次のサーバーと環境を用意しました。
- サーバーA(通常のMCPサーバー):
slow_report(seconds)。指定した秒数だけ待って、完了時刻を返す。 - サーバーB(Tasks対応):同じツールを、2026-07-28のTasks拡張で実装。拡張を宣言したリクエストには、すぐ
resultType: "task"とIDを返す。そうでなければ、サーバーAと同じように待ってから返す。 - どちらも依存ライブラリなしのNode.jsで書いたサーバーです。標準入力と標準出力で話します(stdio)。
- 起動はClaude Code 2.1.310の
claude -p --input-format stream-json。モデルはHaikuです。各イベントが届いた時刻を記録しました。 - お題は「
slow_reportを呼んで。待たずに、dateとechoを実行して。レポートが届いたら中身を教えて」。裏に回った瞬間に、モデルが次の手を打てるかを見ています。
対話画面(TUI)ではなく、スクリプトから動かせる-pモードで測っています。公式文書によると、このモードで裏に回すにはCLAUDE_AUTO_BACKGROUND_TASKS=1が要ります。対話セッションでは要りません。
コードと全データは、experiment.zipに入れました。
結果1:自動バックグラウンド化は、本当に閾値の時間ちょうどで動く
サーバーAに40秒の仕事を頼み、閾値をCLAUDE_CODE_MCP_AUTO_BACKGROUND_MS=10000(10秒)にしました。3回やって、結果はそろっています。
| 回 | 呼び出し→「裏に回った」と返るまで | 仕事の完了(呼び出しから) |
|---|---|---|
| 1 | 10.0秒 | 40.0秒 |
| 2 | 10.0秒 | 40.0秒 |
| 3 | 10.0秒 | 40.0秒 |
10.0秒たったところで、Claude Codeが次のようなメッセージをモデルに返しました。
MCP tool "slow/slow_report" is still running after 10s. It was moved to the
background as task kb2s6i6p9 and keeps running; you'll receive a notification
with the result when it completes. You can keep working in the meantime.
To stop it, use TaskStop with task_id "kb2s6i6p9". Note: it does not survive
exiting this session.
モデルはこれを受けて、すぐdateとechoを実行しました。裏に回ってから約1秒後です。
そして、仕事が終わった40秒の時点。誰も何も送っていないのに、モデルが自分で次のターンを始めて、レポートの中身を答えました。完了通知をきっかけにモデルが動くことを、3回とも確認できました。
既定の設定でも測りました。閾値を触らず、150秒の仕事を頼みます。
| 条件 | 裏に回ったと返るまで | 仕事の完了 |
|---|---|---|
| 既定(閾値120秒)・150秒の仕事 | 120.0秒 | 150.0秒 |
公式文書どおり、ちょうど2分でした。つまり、既定のままだと、2分より短い仕事は裏に回りません。たとえば90秒かかるツールなら、90秒間Claudeは待たされます。
「90秒ならちょっとなあ」と思うなら、閾値を下げられます。
{
"env": {
"CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS": "15000"
}
}
これをsettings.jsonに入れれば、15秒以上かかる呼び出しが裏に回ります。ただし、あまり短くすると、すぐ終わるツールまで毎回裏に回ってしまいます。結果の受け取りが「通知が来てから」になるので、短いツールではかえって手間が増えます。数十秒〜数分のツールが対象、くらいの感覚で決めるのがよさそうです。
結果2:MCP Tasksは、受付と同時にモデルへ制御が戻る
次はサーバーB(Tasks対応)に、同じ40秒の仕事を頼みました。こちらは閾値を設定せず、3回試しました。
| 回 | 呼び出し→「裏に回った」と返るまで | 仕事の完了 | サーバーへのtasks/getの回数 |
|---|---|---|---|
| 1 | 0.0秒 | 40.1秒 | 21 |
| 2 | 0.0秒 | 40.0秒 | 21 |
| 3 | 0.0秒 | 40.1秒 | 21 |
待ち時間は0.0秒です。サーバーが受付の応答を返した瞬間に、Claude Codeが「サーバー側のタスクとして開始した」とモデルに伝えました。
MCP tool "slow/slow_report" started server-side task task-1. It was moved to
the background as task k5eq38tfo and keeps running on the server; you'll
receive a notification with the result when it completes, and you can keep
working in the meantime. ...
その間、Claude Codeは約2秒おきにサーバーへtasks/getを送り、状態を聞き続けていました。サーバーが「2000ミリ秒ごとに聞いてね」(pollIntervalMs: 2000)と返していたので、その通りの間隔です。21回目のgetでcompletedが返り、結果がモデルに渡りました。
通信の中身も見ておきましょう。サーバーBが受け取ったtools/callのメタデータには、こんな宣言が入っていました。
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"roots": { "listChanged": true },
"elicitation": { "form": {}, "url": {} },
"extensions": { "io.modelcontextprotocol/tasks": {} }
}
}
このextensionsの中のio.modelcontextprotocol/tasksが「このリクエストの結果は、taskの形で返してもいいですよ」の合図です。サーバーBはこれを見たときだけ、taskを返します。
ここまでで気づいたかもしれません。Tasksは仕事を速くしていません。40秒かかる仕事は、やっぱり40秒かかります。変わったのは、その40秒の間、Claudeが待たされるかどうかだけです。
結果3:条件が合わないと、Tasks対応サーバーでも「ただの待ち」になる
ここは、実測してよかったなと思ったところです。
サーバーBに頼むのは同じ40秒の仕事。では、Claude Code側の条件を変えるとどうなるでしょう? 実際に変えて確かめました。
| 条件 | 裏に回ったと返るまで | taskが作られたか |
|---|---|---|
ふつうの条件(CLAUDE_AUTO_BACKGROUND_TASKS=1) | 0.0秒 | 作られた |
CLAUDE_AUTO_BACKGROUND_TASKSを付けない(-pモード) | 40.0秒(待たされた) | 作られない |
CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS=0 | 40.0秒(待たされた) | 作られない |
CLAUDE_CODE_DISABLE_MCP_TASK_BACKGROUND=1 | 40.0秒(待たされた) | 作られない |
3つの「待たされた」は、どれもサーバーがおかしくなったわけではありません。サーバーBは拡張を宣言していないリクエストには普通の結果を待ってから返す作りです。今回はその通りに動いたんです。
注目してほしいのは、接続の最初の問い合わせ(server/discover)には、3つとも拡張の宣言が入っていたことです。それなのに、実際のtools/callには宣言が付いていませんでした。
server/discover の clientCapabilities : roots, elicitation, extensions(tasks)
tools/call の clientCapabilities : roots, elicitation ← extensions がない
つまり、サーバーが「接続したとき、このクライアントはTasksに対応していた」と覚えておいて、それをあてにしてtaskを返すと、条件を外した呼び出しでおかしくなります。実際、Claude Codeの公式文書には、拡張が付いていないのにtaskを返すとUnsupported result type 'task' for tools/callというツールエラーになると書かれています。仕様でも、サーバーは「今回のリクエスト」に宣言があるときだけtaskを返してよいとされています。
同じ理由で、待たせたいときの設定も効いています。CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS=0は、自動バックグラウンド化を切る設定ですが、同時にTasksの宣言も外れました。
結果4:入力を閉じると、裏の仕事は止まる
最後に、公式文書が注意していることを確かめました。スクリプトからClaude Codeを動かすとき、入力(標準入力)を閉じると、裏の仕事を待たなくなるという話です。
最初のターンが終わったところで、わざと入力を閉じました。
| サーバー | 入力を閉じたあと |
|---|---|
| A(通常、10秒で裏に回る) | 仕事は止まった。完了せず、通知はstopped |
| B(Tasks) | 同じく止まった。サーバーへのtasks/getは6回で途切れた |
どちらも40秒の仕事が終わる前でした。
Bで止まったのは、サーバーBが同じプロセスの中のタイマーで仕事をしているからです。stdioのサーバーは、Claude Codeが終了するとプロセスごと止まります。これは公式文書にも書かれている動きです。 一方、公式文書によると、リモート(HTTP)のサーバーならタスクは終了後も続き、同じセッションを再開すると追いかけ直せるそうです。ここは今回測れていません(後述)。
ここは覚えておきたいところ。IDを返す(Tasks)ことと、仕事が本当に続くことは別です。 仕事の本体をサーバーのプロセスの外(別のワーカーや外部のジョブ基盤)に置いて初めて、Claude Codeを閉じても続きます。
で、どっちを使えばいい?
場面ごとに整理します。
| こんなとき | おすすめ | 理由 |
|---|---|---|
| 他人が作ったMCPサーバーの、たまに数分かかるツール | 自動バックグラウンド化 | サーバーを触れない。閾値を環境変数で調整 |
| 自作のツール。数十秒〜2分程度 | 自動バックグラウンド化。閾値を下げる | 実装が要らない。通知で結果が届く |
| 自作のツール。受付した直後から別の作業をしたい | MCP Tasks | 待ち時間が0秒。実測で確認 |
| Claude Codeを閉じても仕事を続けたい | MCP Tasks(+リモートで外部ワーカー) | 自動バックグラウンド化は、セッションを終えると追跡が消える |
| 外部APIがジョブIDを返す(動画の変換、バッチ処理など) | MCP Tasks | 外部のジョブIDとtaskIdを対応づけやすい |
| 途中で人間に確認を取りたい | どちらも可。再接続をまたぐならTasks | Tasksはinput_requiredで待てる |
| サブエージェントから呼ぶ | どちらも使えない | 公式文書で対象外 |
claude -p "..."の一発実行 | どちらも既定では使えない | 最後まで待つ前提で作る |
| VS Code拡張・デスクトップアプリ | Tasksは使えない | 現行の公式文書で対象外。通常の呼び出しの扱いは、そのアプリで確認 |
迷ったら、まず自動バックグラウンド化で足りるかを考えてください。サーバーを触らずに済みますし、2分が長すぎれば閾値を下げられます。Tasksを選ぶのは、「閉じても続けたい」「ジョブIDを持ちたい」といった、自動バックグラウンド化では実現できない理由があるときです。
サーバーを作る人向け:Tasks対応の最小サンプル
自分のMCPサーバーをTasks対応にするなら、どこを押さえればいいのでしょう? 実測に使ったserver-tasks.mjsから、要点を抜き出しました。全体はexperiment.zipに入っています。
まず、server/discoverで拡張を宣言します。
if (method === "server/discover") {
send({
jsonrpc: "2.0", id,
result: {
resultType: "complete",
supportedVersions: ["2026-07-28"],
capabilities: { tools: {}, extensions: { "io.modelcontextprotocol/tasks": {} } },
_meta: { "io.modelcontextprotocol/serverInfo": { name: "slow-tasks", version: "0.1.0" } },
},
});
}
tools/callでは、そのリクエストに宣言があるかを毎回見ます。あればtaskを返し、なければ普通に待ちます。
const TASKS = "io.modelcontextprotocol/tasks";
const hasTasksCap = Boolean(
params?._meta?.["io.modelcontextprotocol/clientCapabilities"]?.extensions?.[TASKS]
);
if (hasTasksCap) {
// 仕事を始めて、IDだけ先に返す
send({ jsonrpc: "2.0", id, result: {
resultType: "task",
taskId, status: "working", statusMessage: "accepted",
createdAt, lastUpdatedAt: createdAt,
ttlMs: 3600000, pollIntervalMs: 2000,
}});
} else {
// 宣言がないクライアントには、結果が出るまで待ってから返す
}
そしてtasks/getで状態を返します。終わったら、completedと一緒に、元のツールの結果をresultに入れます。
// 完了前
{ resultType: "complete", taskId, status: "working", ..., pollIntervalMs: 2000 }
// 完了後
{ resultType: "complete", taskId, status: "completed", ...,
result: { content: [{ type: "text", text: "report done..." }] } }
ここでのresultType: "complete"は「tasks/getという呼び出しへの応答が完了した」という意味です。仕事が終わったかどうかはstatusで見ます。この2つを混ぜないでください。
実装では、次の点でつまずきがちです。
- taskIdは
tasks/getで引けるようになってから返す。 DBに書く前にIDだけ返すと、直後のgetが失敗します。 - 仕事の本体はプロセスの外に出す。 このサンプルは教材用で、プロセスが落ちるとtaskも消えます。本番ではワーカーとデータベースを分けてください。
- Claude Codeは通知(
notifications/tasks)を使わず、tasks/getで聞きに来ます。 公式文書の記述です。プッシュ通知だけ実装しても、Claude Code向けには足りません。 - 認可も忘れずに。 taskIdを知っているだけで結果が取れてしまう作りにしないでください。
旧版(2025-11-25)のコードからの書き換え箇所は移行の記事に一覧があります。
自分で試すなら
実験コードをそのまま動かせます。
# experiment.zip を空のフォルダに展開してから
cd <展開したフォルダ>
./run-all.sh # 14条件を並列実行。Claude Codeのログインが必要。モデルを14回呼ぶ
python3 summarize.py /tmp/exp-longmcp/out > summary.json
1条件だけ見たいなら、こうです。
SERVER=server-tasks.mjs node run.mjs my-try 40 \
'{"CLAUDE_AUTO_BACKGROUND_TASKS":"1"}' \
'Call the slow_report tool with seconds=40. Then run `date +%T`.'
自分のサーバーをclaude mcp addで登録して、対話画面で試すのもおすすめです。/tasksを開くと、裏で動いているものが見えます。stdioサーバーのtaskには「runs on the server」という表示が付きます。
測れていないこと
ここまでの実測にも、限界はあります。次の点は確かめていません。
- 対話画面(TUI)での動作。 今回は
-pモードだけです。TUIではCLAUDE_AUTO_BACKGROUND_TASKSが不要など、条件が違います。 - HTTPサーバー。 stdioだけです。リモートのtaskがClaude Code終了後も続くこと、
claude --resumeで追い直せることは、公式文書の記述どおりかどうか未確認です。 - サブエージェントからの呼び出し。 公式文書の記述を信頼しています。
- 途中の入力待ち(
input_required)、取り消し、TTL切れ。 - 他のモデル、他のバージョン。 Haiku、Claude Code 2.1.310のみ。1条件につき1〜3回です。
- 最小サーバーでの結果。 実運用の大きなサーバーで同じとは限りません。
また、MCP Tasksは公開ベータで、使えるかどうかはアカウント・セッションの種類・feature flagにも左右されます。今回は動きましたが、あなたの環境で必ず動くとは言えません。
まとめ
- Claudeが待たされなくなるのは、自動バックグラウンド化なら閾値の時間後(既定120秒)、Tasksなら受付と同時。実測は10.0秒・120.0秒・0.0秒でした。
- 仕事は速くならない。 変わるのは待ち方です。
- サーバーを触れないなら、自動バックグラウンド化で足ります。閾値は
CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで変えます。 - Tasksが向くのは、受付直後から進めたい、閉じても続けたい、外部ジョブIDを持ちたいとき。サーバー側の実装と、プロセスの外の仕事の置き場が要ります。
- Tasks対応でも、呼び出しごとの宣言がなければ待ち呼び出しに戻ります。サーバーは接続時の記憶ではなく、そのリクエストの宣言を見てください。
- スクリプトから使うときは、入力を閉じない。閉じると裏の仕事は止まります。
「待たずに進める」はツールを速くする話ではなく、待っている間にエージェントが何をできるようにするかの話でした。まずは自分のツールが何秒かかるのかを測って、2分より短いなら閾値を下げてみる。そんなところから始めてみてください。
参考にした一次資料
- Claude Code MCP公式文書(自動バックグラウンド化、MCP tasks、サーバー作者向けの節):2026-10-25に取得
- Claude Code headless公式文書
- MCP 2026-07-28 Discovery
- MCP 2026-07-28 Lifecycle(拡張の交渉)
- ext-tasks 2026-07-28仕様(固定commit)
- MCP Tasks概要