チェーンと南京錠で固定されたスマートフォン
現場の実践

依存ゼロ設計の落とし穴とサプライチェーン障害対応の実務

目次を見る

クラウド上で動くツールやCLIの依存パッケージ管理を任されている運用担当者、SREに向けた内容です。あるOSS開発者が公開したMCPクライアント開発の記録には、依存パッケージをゼロにした結果として発生したコストが具体的に記録されていました。この記録は、依存管理のトレードオフを運用視点で見直すきっかけになります。

記録の主はmcptoonというCLIツールの開発者です。MCP(Model Context Protocol、AIエージェントと外部ツールを繋ぐ標準プロトコル)のサーバーとAIエージェントの間に立つツールで、pyproject.tomlのdependenciesを空配列にする、つまりサードパーティ製ライブラリを一切使わない方針を選びました。

何が起きたか

依存ゼロを選んだ結果、標準ライブラリだけで全機能を実装することになりました。HTTPクライアントはrequestsの代わりにurllibで約200行、CLIパーサーはclickの代わりにargparseで約400行、バリデーションはpydanticの代わりに約300行の手書きコードが必要になったと記録されています。

これは開発工数の話にとどまりません。運用の現場で同じ選択をした場合、影響はコードの保守性、障害発生時の原因特定速度、セキュリティパッチ適用の負荷にまで及びます。手書きのHTTP通信処理には、リトライロジックやSSE(Server-Sent Events、サーバーからの継続的なデータ配信方式)のエラーハンドリングが自前実装として埋め込まれており、ここにバグがあれば気づきにくいという課題も残ります。

なぜこの選択に至ったか

発端はuvというPythonパッケージ管理ツールで発生したセキュリティインシデントでした。推移的依存関係(自分が直接使っていないが、依存先がさらに依存しているパッケージ)にサプライチェーン脆弱性が見つかり、多数のプロジェクトが影響を受けたという事例です。

この手の障害は、自分のコードに問題がなくても発生します。上流のどこかのメンテナが悪意ある変更を混入させる、あるいは単純なミスをする、それだけで下流の全プロジェクトが巻き込まれます。npmのevent-streamパッケージ事件や、Pythonのctxパッケージなりすまし事件など、同種の事例はエコシステムを問わず繰り返されてきました。

この開発者は自分のpip installの履歴を振り返り、過去1年で導入したパッケージ数が数百に上る一方、そのうちの依存ツリーを監査した数はゼロだったと述べています。この非対称性への危機感が「サードパーティ製インポートは一切禁止」というルールにつながりました。

つまり依存ゼロは「軽量化のため」ではなく「攻撃対象領域(アタックサーフェス)を減らすため」の選択です。ここを混同すると対策の方向を誤ります。

自分のプロジェクトが該当するか確認する方法

自分が管理するサービスやツールが同種のリスクを抱えているか、次の手順で確認できます。

# Pythonプロジェクトの依存ツリーを可視化する
pip install pipdeptree
pipdeptree --warn silence

# 既知の脆弱性をスキャンする
pip install pip-audit
pip-audit

Node.js環境であればnpm ls --allで依存ツリーを、npm auditで既知の脆弱性件数を確認できます。まず直接依存の数と推移的依存の数を数えてみると、想像より多いことに気づく場合があります。

加えて、pyproject.tomlpackage.jsonのlockファイル(poetry.lockpackage-lock.jsonなど)の更新日時も確認してください。長期間更新されていないlockファイルは、既知の脆弱性が修正済みのバージョンに追従できていない可能性を示します。

対策の手順

依存ゼロという極端な選択をせずとも、運用現場で取れる現実的な対策があります。

  • 依存ツリーの棚卸しを定期実行する: pip-auditnpm auditをCIパイプラインに組み込み、脆弱性のあるパッケージをマージ前に検出する
  • 推移的依存の固定: lockファイルでバージョンを固定し、意図しないアップデートによる混入を防ぐ
  • 依存の最小化ではなく可視化を優先する: 「使っていないのに入っている」パッケージをpipdeptreedepcheckで洗い出し、削除する
  • 重要な機能ほど自前実装のリスクとメリットを天秤にかける: HTTP通信のリトライロジックのように障害時の挙動が重要な部分は、枯れたライブラリに任せた方が障害対応の記録・知見が豊富な場合が多い
  • インシデント発生時の切り戻し手順を用意する: 特定パッケージのバージョンで障害が起きた場合、即座に前バージョンへロールバックできるようlockファイルの世代管理をしておく

障害対応の観点では、依存パッケージ由来の障害は「自分のコードのバグではない」ため原因特定が遅れがちです。監視ダッシュボードにパッケージバージョン情報やビルドハッシュを記録しておくと、障害発生時に「いつのデプロイから壊れたか」の切り分けが早くなります。

まとめ

依存ゼロという選択は、HTTPクライアントやCLIパーサーを合わせて1000行前後を自前実装するコストと引き換えに、サプライチェーン攻撃のリスクを構造的に排除する試みでした。

運用担当者にとっての教訓は、極端な選択の是非ではなく、自分のプロジェクトの依存ツリーを可視化する習慣です。まずはpip-auditまたはnpm auditを一度実行し、既知の脆弱性件数を数字で把握するところから始められます。

そのうえで、依存を減らすべき箇所と、枯れたライブラリに任せるべき箇所を分けて判断する。この線引きが、障害対応の負荷とセキュリティリスクのバランスを取る現実的な落としどころです。

参考

Zero Dependencies, 250KB, 486 Tests: What I Learned Building an MCP Client

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。