0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Agent Plugins 1.0で持ち運べるのは「梱包」まで。実行前のtrust preflightを作る

0
Posted at

UIレビュー用のskillとPlaywright系MCPを一つのdirectoryへまとめ、VS CodeとCLIの両方へ配りたい。Agent Plugins 1.0は、こういう用途にかなり素直なpackage形を用意しています。

ただし、両方のhostがpackageを認識した時点で「portableになった」と判定するのは早いです。

持ち運べたのはskillやMCP設定の梱包です。file writeの承認、network access、secretの扱い、sandboxまで同じになったわけではありません。自分なら本作業に入る前に、package検査とは別のtrust preflightを通します。

この記事では、次の順番を使います。

package validation
  -> capability inventory
  -> host policy check
  -> read-only canary
  -> negative tests
  -> accept / reject per host

仕様が運ぶ範囲を先に固定する

例として、frontend review用pluginを次のdirectoryへまとめます。

frontend-review-plugin/
├── plugin.json
├── skills/
│   └── ui-review/
│       └── SKILL.md
└── mcp.json

plugin.jsonがpackageの入口、skills/が再利用する手順、mcp.jsonがMCP接続の設定面です。具体的なfieldや必須・任意の条件はAgent Plugins 1.0.0の仕様に合わせます。ここでは仕様にないfieldを足して、permission設定まで表現できるようには見せません。

directory shapeが揃えば、skillとtoolを一つの単位として配りやすくなります。ここで揃うのはpackage contractです。host上のpermission systemやsandbox、trust判断は別に確認します。

GitHub側にはVS Code、Copilot CLI、Copilot appでの対応に加え、managed settingsやMCP allowlistの運用があります。これは具体的な実装例として参考になりますが、別のagent hostにも同じcontrolがあるとは限りません。

「portable」の目的語を曖昧にしないほうがよいです。

  • portable package: 同じartifactを配布・認識できる
  • equivalent execution: 同じ権限、制限、secret処理で動く

前者を確認しただけでは、後者のtestは終わっていません。

検査を4層に分ける

自分はpackageの導入判定を4層へ分けます。この4層はAgent Pluginsの公式用語ではなく、チーム側の運用です。

検査するもの 主な所有者
package contract 必須file、JSON syntax、skill path、参照先 仕様とpackage作者
capability inventory MCP server、command / URL、環境変数、外部通信 package作者と導入者
host policy install元、allowlist、write範囲、network、approval、sandbox 各hostと導入チーム
evidence prompt / deny、filesystem diff、uninstall後の残存物 導入チーム

plugin.jsonのsyntaxが正しくても、mcp.jsonから起動するcommandが安全とは限りません。要求能力に問題がなくても、host側にdenyする仕組みがない場合があります。そこで検査結果を層ごとに残します。

package作者が責任を持てる範囲と、実行hostでしか確認できない範囲を一つの「安全」というlabelへ丸めないようにします。

最初のpreflightは地味でいい

まずはfileの存在、JSON syntax、差分を確認します。

set -eu

plugin_dir="frontend-review-plugin"

test -f "$plugin_dir/plugin.json"
test -d "$plugin_dir/skills"
jq -e . "$plugin_dir/plugin.json" >/dev/null

test ! -f "$plugin_dir/mcp.json" \
  || jq -e . "$plugin_dir/mcp.json" >/dev/null

git diff -- \
  "$plugin_dir/plugin.json" \
  "$plugin_dir/mcp.json" \
  "$plugin_dir/skills"

jqで分かるのはJSONとして読めるかだけです。schema validationの代わりにはなりません。公式validatorが提供されている環境では、そちらも使います。

最後のgit diffは自動判定ではなく、人が見るために残しています。とくに確認したいのは次です。

  • 想定外のskillが増えていないか
  • mcp.jsonへ知らないserver、command、引数、URLが入っていないか
  • secretの実値がpackageへ混ざっていないか
  • package外のpathを参照していないか

MCP serverの名前だけを見て終えると、実行commandや接続先の変更を見落とします。plugin更新時にはlockfileのように差分を読み、能力が増えたら再承認する運用が必要です。

trust条件はteam-owned policyへ置く

permissionやnetwork条件を、仕様外のfieldとしてplugin.jsonへ押し込むのは避けます。代わりに導入チームが検査用fileを持ちます。

# plugin-policy.yml
# Agent Pluginsの公式schemaではない。導入前の期待条件を記録するfile。
package: ./frontend-review-plugin

expected_mcp_servers:
  - playwright

secrets: env-only

hosts:
  vscode:
    write_scope: worktree-only
    network: deny-unless-reviewed
    approval: required

  copilot-cli:
    write_scope: worktree-only
    network: deny-unless-reviewed
    approval: required

このYAMLをVS CodeやCopilot CLIが直接読み込む想定ではありません。「このhostを採用するなら満たしてほしい条件」をreview可能な形にしただけです。

各項目の結果はpass / fail / unsupported / not-testedで残します。hostに該当controlがなければ、package側で安全そうな説明を足すのではなくunsupportedです。チームの必須条件なら、そのhostはrejectします。

client固有のinstall commandはversionで変わり得るため、ここでは固定しません。packageを読み込む部分は各clientの現行手順に従い、読み込み後の検査を同じにします。

read-only canaryで「認識」と「実行」を分ける

最初から実装taskを渡すと、skill選択、command実行、file変更、network accessが一度に起きます。どこでpolicyが効いたのか追えません。

最初のtaskは、書き込みもnetworkも不要なものに絞ります。

このrepositoryのpackage.jsonを読み、scripts名だけを列挙する。
file変更、command実行、network accessは行わない。

VS CodeとCopilot CLIで同じcanaryを走らせ、結果を別々に記録します。

check VS Code Copilot CLI
package recognized pending pending
skill selected pending pending
file changed pending pending
command executed pending pending
network used pending pending

package recognizedskill selectedは別々に判定します。package一覧へ出たことは、意図した場面でskillが選ばれた証拠にはなりません。

同様に、agentが会話で「変更していません」と答えただけでは弱いです。git statusやfilesystem diff、host側の実行logを証拠にします。

negative testは3つだけ先に通す

canaryが通ったら、禁止したい動作を小さなtest環境で要求します。本番repositoryや本物のcredentialは使いません。

1. file write

使い捨てのworktreeでfile変更を依頼し、approval promptまたはdenyが記録されるかを見ます。変更できたかどうかだけでなく、誰が、どのpathに、どの承認を経て書いたかを残します。

2. allowlist外のMCP

未許可のMCP serverを使うtaskを与えます。期待する結果は、利用不可として止まるか、明示的なapprovalへ進むことです。勝手に代替serverへ接続した場合はfailにします。

3. 未承認network access

未承認URLへのaccessを要求し、team policyどおりdenyまたはapprovalになるかを確認します。agentの返答ではなく、hostのprompt、deny記録、network logを見ます。

追加で、必要な環境変数を外した状態も試します。secretを推測したり別の保存場所から拾ったりせず、入力不足として停止するのが期待値です。

最後にuninstallし、skill、MCP設定、credential参照が残っていないかを確認します。導入時だけ見ていると、この後片付けを忘れがちです。

hostごとにaccept / rejectする

結果はpackage単位の一つの判定ではなく、hostごとに残します。

| check | VS Code | Copilot CLI |
|---|---|---|
| package recognized | not-tested | not-tested |
| read-only canary | not-tested | not-tested |
| file-write approval | not-tested | not-tested |
| MCP allowlist | not-tested | not-tested |
| network policy | not-tested | not-tested |
| clean uninstall | not-tested | not-tested |

上の表はtemplateです。実測した結果ではありません。最初からpassで埋めず、検査していない項目をnot-testedのまま見せます。

判定に残す情報はこの程度で足ります。

package commit
host / version
install source
capability inventory
canary result
negative-test result
filesystem diff
uninstall result
known unsupported controls

VS Codeでは必須条件を満たし、CLIではnetwork controlがunsupportedだった。そういう結果なら、VS Codeだけをallowlistへ残せばよいです。同じpackageを読めることを理由に、判定まで共通化する必要はありません。

Agent Plugins 1.0を使うと、同じskillとMCPを複数hostへ配りやすくなります。配布先が増えたときに頼れるのは、「どこでも動くはず」という期待より、plugin-policy.yml、canary、negative testの記録です。

自分なら導入完了の条件をpackage recognizedには置きません。hostごとの証拠が揃ったところを完了にします。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?