スキルのレビューは SKILL.md だけでは足りない:dsh-skills-anywhere のディレクトリ・マニフェストでスクリプトと参考ファイルを照合する

SKILL.md が同じでもスクリプトは変わりうる。0.9 の全ファイルのマニフェスト、比較の終了コード 0・1・2、MCP での変更拒否と、一致が証明しないこと。

比較図:タイトルの下に番号のない塗りつぶしの円が 3 つ横に並び、それぞれの下に横線と次の文字があります。expected_sha256 は SKILL.md のみ照合し、他のファイルの変更は対象外。expected_bundle_sha256 はマニフェスト全体を再計算し、追加・削除・変更でエラー、指示は返さない。include_bundle: true は現在のマニフェストを確認用に返し、審査値とは比較しない。下部に出典の注記があります。
MCP ツール open_skill の関連する 3 つのオプションがそれぞれ何を照合するか。noteflowai/dsh-skills-anywhere の commit c011051 の docs/BUNDLES.md に基づいて作成。3 つは並列のオプションで、組み合わせて使えます。expected_bundle_sha256 を渡すと include_bundle も有効になります。並び順は優先度を示しません。

dsh-skills-anywhere 0.9 では、既存の単一ファイルの検査に加えて、上限付きのスキルディレクトリ・マニフェスト(bundle manifest)が追加されました。理由はドキュメントの冒頭に書かれています。SKILL.md が変わっていなくても、その横にあるスクリプトや参考ファイルも変わっていないとは限りません。本記事では、commit c011051 に固定した docs/BUNDLES.md と docs/MCP-COMPATIBILITY.md に基づき、このマニフェストをスキルのレビューにどう組み込むかを説明します。マニフェストを生成・比較するコマンド、3 つの終了コード、MCP で読み込むときに同じディレクトリ識別を要求する方法、マニフェストの形式と上限、そしてダイジェストの一致が証明しないことを扱います。

本サイトの Skills Anywhere:各 AI ツールの SKILL.md を 1 つのカタログにまとめ、dsh や MCP から必要に応じて読み込む ではプロジェクト全体と、そのファイル検査およびハッシュ指定での読み込みを紹介しました。以下の動作の説明はすべてプロジェクトのドキュメントによるものです([O]、プロジェクト自身の説明)。ドキュメント中のテスト結果はプロジェクトの報告であり、本サイトはこれらのコマンドやテストを実行していません。

SKILL.md だけを確認しても足りない理由

ドキュメントが引用する Agent Skills 仕様(ドキュメントによれば 2026-09-14 に確認)は、スキルを SKILL.md と任意の scripts、references、assets からなるものとしています。MCP ツール open_skill の既存の引数 expected_sha256 が照合するのは SKILL.md だけです。MCP-COMPATIBILITY.md には、このハッシュは参照先のスクリプトやほかのファイルを対象とせず、作成者の身元も確認しないと書かれています。そのため、レビューで読んだ SKILL.md は 1 文字も変わっていないのに、それがエージェントに実行させるスクリプトは差し替えられている、ということが起こりえます。

ディレクトリ・マニフェストは、指定したディレクトリ配下のすべての通常ファイル(regular file)について、相対パス、バイト長、SHA-256 を記録します。隠しファイルやバイナリファイルも含まれます。ドキュメントは、これがプロジェクト独自の内容識別の形式であり、Agent Skills 仕様の一部だと主張するものではないとも述べています。

ローカルと CI でマニフェストを生成・比較する

ドキュメントに示されている 2 つのコマンドは次のとおりです。コメントは原文のまま残しています。

# Keep both the review manifest and output logs outside the skill directory.
npx -y dsh-skills-anywhere@0.9.0 bundle /path/to/skill --json > reviewed-bundle.json
# Review the actual files, then retain the manifest in your review record.
npx -y dsh-skills-anywhere@0.9.0 bundle /path/to/skill \
  --against reviewed-bundle.json --json

1 つ目のコメントは、レビュー用マニフェストも出力ログもスキルディレクトリの外に置くこと、2 つ目は、実際のファイルをレビューしてからマニフェストをレビュー記録に残すことを指示しています。1 つ目のコマンドはレビュー時点のディレクトリのマニフェストを生成し、2 つ目は --against で現在のディレクトリを保存済みのマニフェストと比較します。コマンドにパッケージのバージョン @0.9.0 が書かれているので、npx が実行するツールのバージョンはコマンド自体で固定されます。この節の見出しは “Local review and CI” ですが、ドキュメントに CI の設定ファイルは示されていません。

コマンド終了コードドキュメントに書かれた条件
マニフェストの生成0生成に成功
比較(--against)0ファイルのパスと内容が一致
比較(--against)1差分あり
比較(--against)2無効な入力、不安定な読み取り、またはサポート外のディレクトリ項目

マニフェストの生成に失敗したときの終了コードはドキュメントに書かれておらず、本記事でも推測しません。比較の JSON 出力には added、removed、changed、双方のダイジェスト、現在の manifest が含まれます。名前の変更は、削除されたパス 1 つと追加されたパス 1 つとして表れます。人が読む形式の出力では、入力に由来するパスがエスケープされます。どちらのコマンドも、ほかのスキルのルートを探したり、Git を実行したり、リポジトリを同期したり、スキルのコードを実行したりはしません。

マニフェストの置き場所

保存するマニフェストはスキルディレクトリの外に置く必要があります。中に置くと、マニフェスト自体が入力に含まれてしまいます。シェルのリダイレクトはコマンドの開始前にファイルを作成することがあるため、必ず別のレビュー用ディレクトリへリダイレクトしてください。上のコマンドは reviewed-bundle.json を現在のディレクトリに書き込むので、実行時の現在のディレクトリがスキルディレクトリの中にあってはいけません。黙って無視されるファイルはありません。キャッシュや Git のメタデータを含むアプリケーションのチェックアウト全体ではなく、スキル自身のディレクトリを指定してください。

仮の例

以下は本サイトが説明のために設定した状況で、実行結果ではありません。レビュー時のディレクトリには SKILL.md と scripts/review.py だけがあり、その後 scripts/review.py だけが編集されたとします。ドキュメントの取り決めによれば、2 つ目のコマンドは終了コード 1 で終わり、scripts/review.py が changed に表れます。編集が scripts/check.py への名前の変更だった場合は、removed に scripts/review.py、added に scripts/check.py が表れます。ディレクトリにほかの場所を指すシンボリックリンクが追加された場合、ドキュメントはこれを拒否される項目としているので、上の表に従えば結果は 1 ではなく終了コード 2 になるはずです。

受け取ったマニフェストをブラウザーで比較する

Hugging Face のデモページには、作者が用意した、scripts/review.py だけが変わる例があります。例の 2 つのマニフェストは、サイトのビルド時に実際のファイルシステム読み取り処理で生成されたものです。どちらかのマニフェストをダウンロードしてそれに対応するファイルを復元したり、自分の 2 つの JSON マニフェストを開いて差分を確認したりできます。比較の前に、ブラウザーは CLI と同じ正規化で各マニフェストのダイジェストを計算し直します。入力はページのメモリ内にとどまり、アップロード、保存、共有リンクへの追加は行われません。無効な入力は該当する結果を消去し、例へのリセットは保留中のファイル読み取りを取り消します。このページが比較するのは記録済みのマニフェストです。お使いのファイルシステムを確認することも、ローカルの MCP サーバーに接続することもできません。

MCP で読み込むときに同じディレクトリ識別を要求する

レビューを通ったら、マニフェストのダイジェストを付けて open_skill を呼び出します。

{
  "name": "your-discovered-skill-name",
  "expected_bundle_sha256": "<manifest.sha256 from your reviewed bundle>"
}
  • open_skill は指示を返す前にディレクトリ・マニフェストを計算し直します。ファイルが 1 つでも追加・削除・変更されていればダイジェストが一致せず、呼び出しは指示本文を含まないエラーを返します。返される bundle フィールドは、照合に使ったマニフェストです。
  • 事前に保存したダイジェストがない場合は、include_bundle: true で現在のマニフェストを確認できます。expected_bundle_sha256 を渡すと、このオプションも有効になります。
  • expected_sha256 は引き続き SKILL.md だけを照合し、2 つのダイジェスト引数は併用できます。ディレクトリモードでは、返される指示とそのファイルハッシュは、ディレクトリ・マニフェストに使われたのと同じバイト列から得られます。
  • ダイジェストが一致しても、作成者による現在の無効化設定は引き続き適用されます。
  • ディレクトリのハッシュ計算はオプトインで、スタンドアロンの MCP サーバーの open_skill ツールでのみ使えます。既定のツールやリソースの読み込み、および dsh プロバイダーの動作は変わりません。

MCP-COMPATIBILITY.md は 0.9 の関連テストを報告しています。同じ 4 つの SDK 構成(SDK 1.30.0 と、SDK 2.0.0 の legacy、auto、2026-07-28 固定の 3 通りのネゴシエーション)それぞれで expected_bundle_sha256 を使って読み込み、SKILL.md はそのままでリソースファイルだけを変更し、読み込みが拒否されることを確認します。インストールした npm アーカイブでも 2 世代の SDK で同じ確認を行い、CLI の操作は空の PATH で実行しています。これらはプロジェクト自身のテスト記録です。

マニフェストの形式と上限

形式 skills-anywhere-bundle-1 のフィールドはちょうど 4 つ、schema、sha256、total_bytes、files です。各ファイルの項目はちょうど path、bytes、sha256 からなります。行はスラッシュ区切りの相対パスで、JavaScript の文字列の昇順に並びます。パスは重複せず、あるパスが別のファイルの親パスを兼ねることもできません。Unicode の正規化やロケールに依存する並べ替えは行いません。ディレクトリのダイジェストは、次の UTF-8 JSON タプル(空白なし)の SHA-256 です。

["skills-anywhere-bundle-1",[["relative/path",byte_length,"file_sha256"],...]]

ディレクトリの絶対パス、パッケージのバージョン、タイムスタンプ、JSON の書式はこの識別に含まれません。そのため、同一のファイルを別のディレクトリへ移してもダイジェストは変わりません。SKILL.md は存在し、有効な UTF-8 である必要がありますが、マニフェストの生成では frontmatter を検証しません。パーサーの結果は check コマンドで確認します。ほかのファイルはバイナリでもかまいません。

項目上限
ファイル数512
ディレクトリ項目の総数1,024
パスの階層12
SKILL.md128 KiB
そのほかの 1 ファイル16 MiB
ディレクトリの合計32 MiB
受け取るマニフェスト 1 件1 MiB
相対パス 1 つ1,024 UTF-8 バイト

制御文字、バックスラッシュ、コロン、パストラバーサルの要素、Unicode の置換文字を含むパスは拒否されます。インストール先のルート自体がシンボリックリンクの場合は 1 回だけ解決され、その配下のシンボリックリンクや、パイプのような特殊ファイルは拒否されます。ファイルの読み取り、ディレクトリの列挙、最後のメタデータ確認で、検査中に起きた変更を検出します。

制約と注意点

  • ダイジェストの一致が示すのは、記録されたファイルのパスと内容のバイト列が同じだということだけです。作成者を認証せず、コードが安全かどうかも判断せず、モデルが指示に従ったことも証明しません。
  • 空のディレクトリ、パーミッション、所有者、ディレクトリ外のファイル、インストール済みのパッケージ、インタープリターのバージョン、リモート URL、動的に読み込まれる内容は、ダイジェストの対象外です。
  • Git のリビジョンを推定も認証もしません。リポジトリの出所が重要な場合は、別途レビューしたソースのリビジョンを記録しておいてください。
  • 検査はディレクトリを凍結しないため、その後のリソース読み取りや実行では異なる内容が読まれる可能性があります。ドキュメントは、安定した管理下のチェックアウトを使い、実際に使う時点で自分の実行ポリシーを適用するよう勧めています。この機能が検出するのは読み込み時・検査時のディレクトリの変化であり、サンドボックスでも防御能力のベンチマークでもありません。
  • 既定のツールやリソースの読み込みではディレクトリのハッシュを計算しません。引数を明示しなければ、この保護はありません。
  • MCP の互換性テストは SDK レベルのプロトコル相互運用性を確かめるもので、Claude Code、Cursor、Codex など個別アプリケーションのすべてのリリースを保証するものではありません。ドキュメントの 0.8 の説明には、このパッケージが stdio で提供され、0.8 では HTTP トランスポート、認可、リモートホスティングを追加していないと記されています。
  • 本記事は commit c011051 のドキュメントに基づき、コマンドは 0.9.0 に固定しています。以降のバージョンでは引数、フィールド、終了コードが異なる可能性があります。

リンク