MCP・CLI・Skill の使い分け——「MCP is dead」論争に実践的に決着をつける
はじめに
2026年2月末、「MCP is dead. Long live the CLI」というブログ記事が話題になっていた。著者は「MCPは不要な複雑性を増やすだけで、CLIで十分だ」と主張している。
この主張には一理あると思いつつ。同時に、「じゃあMCPはゼロでいいか」と問われると、それも違う気がする。
自分はここ数ヶ月、Claude Codeをベースにしたマルチエージェントシステムを使って遊んでいる。その過程でMCP・CLI・Skill(Claude Codeのスキル機能) という三つのレイヤーを使い分けるようになった。この記事は、その実体験をもとに「どこで何を使うか」の判断基準を整理したものだ。
「MCP is dead」論文の要旨
Holmes氏の主張を要約すると次の三点になる。
1. 冗長なプロトコル
LLMはすでにCLIの使い方を大量のmanページやStack Overflowから学習済みだ。gitやghやawsはそのまま使える。MCPはこれらに新しい通信レイヤーを被せただけで、本質的な価値を生んでいない。
2. デバッグできない
CLIは人間が同じコマンドを打ってすぐ確認できる。MCPはJSONトランスポート越しになるため、「AIが何をやったか」を人間が追いにくい。
3. 構成可能性がない
CLIはパイプで繋げられる。gh pr list | grep "draft" | wc -l のような合成ができない。MCPツールは個々の呼び出しに閉じていて、Unixパイプのような組み合わせが難しい。
これらはいずれも正しい指摘だ。CLIが存在するサービスに対してMCPを作るのは、確かに無駄が多い。
反論:MCPが本当に必要な場面
しかしHolmes氏が見落としているのは「CLIが存在しないサービス」の問題だ。
自分の例でいうと、デジタル庁が運用する補助金申請システム「Jグランツ」のAPIがある。このAPIには公式CLIが存在しない。Webブラウザか直接HTTPリクエストでしか操作できない。
ここで選択肢は三つある。
- AIに都度「この形式でcurlを叩いて」と指示する
- 薄いPythonスクリプトを書いてCLIを自作する
- MCPサーバーを実装してLLMに直接ツールとして公開する
3番を選んだ。理由は単純で、「LLMからの呼び出し」が主な用途だったからだ。MCPサーバーは「自分専用のCLI」として機能する。人間が操作する機会はほぼなく、LLMが補助金を検索・分析する際に使われる。
# Jグランツ MCP構成(概略)
jgrants_mcp_server/
core.py # search_subsidies, get_subsidy_detail など5ツール
run_stdio.py # stdio transport
search_subsidies(キーワード検索)、get_subsidy_detail(詳細取得)、get_data_summary(統計集計)などのツールを定義した。LLMは「IT導入補助金を探して」という自然言語から、適切なパラメータでこれらを呼ぶ。
「単なるHTTP検索ならcurl+jqの薄いbashスクリプトで十分では?」と思うかもしれない。実際、検索だけならそれで済む。
MCPにした決め手は補助金情報のPDF処理だ。Jグランツの補助金詳細は、募集要項や申請書類がPDFで添付されている。LLMが本当に役立つ分析をするには、そのPDFの中身まで読む必要がある。
# MCPが必要だった処理フロー
search_subsidies → get_file_download_urls
→ バイナリダウンロード
→ Base64デコード → ローカル保存
→ pdfplumber/MarkItDownでMarkdown変換
→ LLMが全文を読んで分析
バイナリ処理、ファイル変換、複数ステップの状態管理——これをAIに都度curlで指示するのは現実的ではない。この一連の処理を「1ツール呼び出し」として抽象化できるのが、MCPの価値だ。
「curl+jqで済む範囲」と「MCPにすべき境界」は、バイナリ処理・ファイル変換・複数APIの連鎖が出てきたタイミングだと感じている。
MCPの本質は「CLIを生やす」ことだ。公式CLIが存在しないAPIに対して、自分専用のCLIインターフェースを定義する技術として捉えると、位置づけが明確になる。curl+jqで済むうちはbashスクリプトで十分。複雑な処理フローが必要になった時点でMCP化を検討するのがちょうどいい。
第三の選択肢:Skill
MCPとCLIの議論に欠けている視点が「Skill」だ。
Claude CodeのSkill機能は、繰り返す操作パターンを~/.claude/skills/{name}/SKILL.mdに記述しておくと、LLMが自動的に参照してくれる仕組みだ。
たとえば、技術トピックをWeb調査してObsidianに保存する手順をSkillにしている。
# SKILL: web-research
## 手順
1. WebSearchで指定トピックを調査する
2. 結果をfrontmatter付きMarkdown(title, date, tags等)に整形する
3. Obsidian Vaultのresearch/配下に保存する
これはWebSearchとWriteという既存ツールの組み合わせだ。新しいサーバーも外部サービスも不要。「調査→整形→保存」というフローを毎回口頭で指示する代わりに、Skillとして固定することで再利用できる。
Skillは操作の仕方を標準化するプロンプトだ。MCPのようにサーバーを立てる必要はなく、.mdファイル一枚で済む。既存ツールで実現できる作業フローの標準化には、Skillが最も軽量な選択肢になる。
三層モデル:何をどこで使うか
整理すると、次の三層モデルになる。
| レイヤー | ツール | 使う場面 |
|---|---|---|
| 既存CLIをそのまま使う | CLI |
git, gh, aws, kubectl など公式CLIがあるもの |
| CLIのないサービスにCLIを生やす | MCP | 公式CLIが存在しないAPI、ブラウザしか用意されていないサービス |
| 繰り返し操作を標準化する | Skill | 毎回同じ手順で操作するもの、ドキュメント参照が必要なもの |
「MCPか、CLIか」という二項対立は誤った問いだ。正しくは「既存CLIがあるか、自前CLIを作る必要があるか、操作パターンを標準化したいか」の三択だ。
CLIっぽいMCP設計原則
MCPを作るとき、「CLIらしさ」を意識すると品質が上がる。Holmes氏が批判するのは「CLIっぽくないMCP」だからだ。
| CLI設計原則 | MCP適用 |
|---|---|
| 1コマンド1責務 | 1ツール1責務(search_and_analyzeではなくsearchとanalyzeを分ける) |
| 失敗時は非ゼロ終了+stderr | エラーは構造化して即返す({"error": "rate_limit", "retry_after": 60}) |
| 冪等性 | 同じ引数で2回呼んでも安全(副作用のある操作は明示的に分ける) |
| 状態を外に持つ | サーバー内部状態を最小に(セッション状態は呼び出し元が管理) |
--dry-run |
destructiveなツールにはpreviewモード(confirm: bool = Falseパラメータなど) |
| タイムアウト | 応答がない=死。必ずtimeoutを設定し、LLMがフリーズしないようにする |
「MCPは複雑」という批判の多くは、このCLI設計原則を無視した実装に起因する。1ツールに複数の責務を詰め込んだり、内部にセッション状態を溜め込んだりすると、Holmes氏の言う「デバッグできない」状況になる。
実践的な線引き
実際に判断するときの問いは次の三つだ。
Q1. 公式CLIは存在するか?
→ Yes: そのまま使う。わざわざMCPにしない。
Q2. LLMが「人間と同じツール」で操作できるか?
→ Yes: CLIで十分。Skillで使い方を標準化するだけでいい。
→ No(ブラウザUI専用、独自API等): MCPを作る価値がある。
Q3. 操作パターンが毎回同じか?
→ Yes: Skillに記述して再利用する。
まとめ
-
CLIがあるなら使え:Holmes氏は正しい。
ghやgitをMCP化するのは過剰だ - CLIがないならMCPを作れ:公式インターフェースがAPIしかない場合、MCPは「自分専用CLI」として機能する
- 繰り返すならSkillで標準化せよ:操作の手順や参照するドキュメントをSkillに記述することで、セッションをまたいだ再利用が可能になる
「MCPは死んだ」のではなく、「CLIがある場所にMCPを作っていた」だけだ。道具には適切な使い場所がある。
本記事はClaude Codeを用いたマルチエージェント開発の実体験をもとにClaudeが9割程度執筆しました。