Agent SDKプラグインをロードする方法
Agent SDKのpluginsオプションでローカルプラグインを読み込む手順。パス指定のルール・複数ソースの組み合わせ・initメッセージでの確認・よくあるトラブルまでまとめました。
Agent SDKがプラグインを読み込む仕組み
プラグインは、Skills・Agents・Hooks・MCPサーバーをひとまとめにして配布できる単位です。
プラグインに入る4種類
Skills
関連するときにClaudeが自律的に呼ぶ能力です。
/plugin-name:skill-nameで直接呼ぶこともできます。Agents
特定タスク向けの専門サブエージェントです。
Hooks
ツール実行などのイベントに反応する処理です。
MCPサーバー
Model Context Protocol経由の外部ツール連携です。
1つのプラグインディレクトリに、この4種類を好きなだけ組み合わせて入れられます。Agent SDKでは、これをコードから直接読み込めます。CLIの/plugin installのようなマーケットプレイス経由のインストール操作はSDK側には無く、query()のpluginsオプションにローカルのディレクトリパスを渡すだけです。マーケットプレイスやリモートリポジトリで配布されているプラグインを使いたい場合も、先に手元へダウンロードしてからそのローカルパスを渡します。
CLIでのプラグイン作成・マーケットプレイスの使い方はClaude Codeプラグイン完全ガイドが扱っています。Agent SDK自体の基本はClaude Agent SDK入門で押さえられます。
動作にはAgent SDK本体が必要です。TypeScriptなら@anthropic-ai/claude-agent-sdk、Pythonならclaude-agent-sdkをインストール済みのプロジェクトで、以下のコード例をそのまま試せます。
npm install @anthropic-ai/claude-agent-sdk
# または
pip install claude-agent-sdkプラグインと単体設定(.claude/)のどちらを使うか
プラグインを作る前に、そもそもプラグイン化が必要かを考える価値があります。Claude Codeには、SkillsやHooksを追加する方法がもう1つあります。プロジェクトの.claude/ディレクトリに直接ファイルを置く単体設定です。
| 方法 | コマンド名の形 | 向いている場面 |
|---|---|---|
単体設定(.claude/ディレクトリ) | コマンド名の形/hello | 向いている場面個人の作業用、プロジェクト固有のカスタマイズ、その場限りの実験 |
プラグイン(skills/ / agents/ / hooks/をまとめたディレクトリ) | コマンド名の形/plugin-name:hello | 向いている場面チームでの共有、コミュニティへの配布、バージョン管理された配布、複数プロジェクトでの再利用 |
自分1人のプロジェクトでしか使わないSkillなら、.claude/skills/に直接置くだけで十分です。複数のプロジェクトやチームで同じSkillsやHooksを配りたくなった時点で、プラグイン化してpluginsオプションから読み込む形に移行する、という判断基準になります。
pluginsオプションでローカルパスを渡す
typeフィールドは"local"固定で、SDKが受け付ける唯一の値です。1つのquery()呼び出しで複数のプラグインを別々のパスから読み込めます。
for await (const message of query({
prompt: "Hello",
options: {
plugins: [
{ type: "local", path: "./my-plugin" },
{ type: "local", path: "/absolute/path/to/another-plugin" }
]
}
})) {
// プラグインのコマンド・エージェント等が使えるようになる
}options = ClaudeAgentOptions(
plugins=[
{"type": "local", "path": "./my-plugin"},
{"type": "local", "path": "/absolute/path/to/another-plugin"},
]
)パスはプラグインのルートディレクトリ、つまりskills/ / agents/ / hooks/ / commands/ / .claude-plugin/の親ディレクトリを指します。パスはルートを指す必要があり、ルート以外を指すと意図した中身が読み込まれません。
パス指定で気をつける3点
| 書き方 | 挙動 |
|---|---|
相対パス("./plugins/my-plugin") | 挙動cwdオプションを基準に解決 |
絶対パス("/home/user/plugins/my-plugin") | 挙動そのまま解決 |
チルダ("~/plugins/my-plugin") | 挙動展開されない。ホームディレクトリを使うならos.homedir()(Node.js)やPath.home()(Python)で自分で絶対パスに組み立てる |
存在しないパスを渡すと、SDKは例外を投げずにそのプラグインを読み飛ばし、セッションを続行します。読み込めたかどうかは、次に説明するinitメッセージで確認します。
initメッセージでロード結果を確認する
セッション開始直後のsystem初期化メッセージ(subtype: "init")には、読み込まれたプラグインの一覧がpluginsフィールドとして入ります。名前とパスの組で構成され、パスが存在しなかったプラグインはここに出てきません。
if (message.type === "system" && message.subtype === "init") {
console.log("Plugins:", message.plugins);
// 例: [{ name: "my-plugin", path: "/absolute/path/to/my-plugin" }]
console.log("Skills:", message.skills);
// 例: ["my-plugin:greet"]
console.log("Commands:", message.slash_commands);
// 例: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
}プラグインが提供するSkillsは、プラグイン名をプレフィックスにした名前空間付きでskillsとslash_commandsの両方に並びます。
読み込みに失敗した理由は、同じinitメッセージのplugin_errorsに入ります。読み飛ばされたパスだけでなく、hooksファイルのように一部だけ読めなかった場合も載ります。
if (message.type === "system" && message.subtype === "init") {
for (const e of message.plugin_errors ?? []) {
// 例: { plugin: "inline[0]", type: "path-not-found", path: "/abs/path/to/my-plugin", message: "..." }
console.error(`${e.plugin} (${e.type}): ${e.message}`);
}
}ディレクトリ自体が読めなかったときは、pluginにプラグイン名ではなくinline[0]のような位置の目印が入ります。pluginsオプションのどの要素かはpathで突き合わせます。typeはpath-not-found(パスが無い)やmanifest-validation-error(マニフェストの不備)などで、未知の値は一般的な失敗として扱う前提の値です。plugin_errorsはTypeScript版のAgent SDK v0.3.283以降で型に載っています。
プラグインのSkillsを名前空間付きで呼び出す
プラグインのSkillは自動的にplugin-name:skill-nameという名前で衝突を避けます。呼び出すときはプロンプトにその名前空間ごと書きます。
for await (const message of query({
prompt: "/my-plugin:greet",
options: {
plugins: [{ type: "local", path: "./my-plugin" }]
}
})) {
if (message.type === "assistant") {
console.log(message.message.content);
}
}CLIの/plugin install my-plugin@marketplaceで既にインストール済みのプラグインも、そのインストール先パスをpluginsオプションへ渡せばSDKから使えます。インストール先は~/.claude/plugins/で確かめます。
複数のプラグインソースを組み合わせる
プロジェクト固有のローカルプラグインと、チームで共有しているプラグインを同時に読み込むこともできます。
import * as os from "node:os";
import * as path from "node:path";
plugins: [
{ type: "local", path: "./local-plugin" },
{
type: "local",
path: path.join(os.homedir(), ".claude", "custom-plugins", "shared-plugin")
}
];プラグインの側がMCPサーバーを持ち、その接続をアプリ側で管理したいときは、skipMcpDiscovery: trueをその要素に付けます。Skills・Hooks・Agents・Commandsは読み込まれ、.mcp.jsonとマニフェストのmcpServersだけが読まれません。このフィールドはTypeScriptのSdkPluginConfigの定義にあります。
プラグインのディレクトリ構成
.claude-plugin/plugin.jsonのマニフェストは任意です。無くてもClaude Codeがディレクトリ構成から中身を自動判定します。
my-plugin/
├── .claude-plugin/
│ └── plugin.json # 省略可。無ければ自動判定
├── skills/
│ └── my-skill/
│ └── SKILL.md
├── commands/
│ └── custom-cmd.md
├── agents/
│ └── specialist.md
├── hooks/
│ └── hooks.json
└── .mcp.jsoncommands/は、フラットな.mdファイルとしてSkillを置く形式です。新しく作るプラグインではskills/を使います。どちらの場所も読み込まれます。
hooks/hooks.jsonに書けるフックはプラグイン専用の形式ではなく、Claude Code本体のイベントハンドラーと同じ書式です。ツール実行の前後やセッション開始時など、対応するイベントの種類と設定の書き方はClaude Code Hooks完全ガイドにまとまっています。hooks/hooks.jsonでは、settings.jsonのhooksオブジェクトと同じ形を、最上位の"hooks"キーで包んで書きます。プラグインに入れておけば、フックの定義がプラグインのディレクトリごと配布されます。
一通りの流れを1つのコード例で見る
maxTurnsを短く区切ると、プラグインが読み込まれているかどうかだけを見る検証用クエリとして使い回せます。本来のタスク用のプロンプトを流す前に、この形の軽いクエリを1回はさむ使い方があります。
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "What custom commands do you have available?",
options: {
plugins: [{ type: "local", path: "./plugins/my-plugin" }],
maxTurns: 3
}
})) {
if (message.type === "system" && message.subtype === "init") {
console.log("Loaded plugins:", message.plugins);
console.log("Available skills:", message.skills);
console.log("Available commands:", message.slash_commands);
}
if (message.type === "assistant") {
console.log("Assistant:", message.message.content);
}
}実行すると、最初に届くinitメッセージでロード済みプラグインの一覧が確認できます。続くassistantメッセージには、Claudeがプラグインの存在を認識した上での応答が入ります。
SDKを動かす前にclaude plugin validateで検査する
プラグインのディレクトリが正しいかは、SDKを動かさなくてもclaude plugin validateで調べられます。claude plugin validate --helpは、プラグインかマーケットプレイスのマニフェスト、またはディレクトリ内のSkills・Agents・Commandsを検証するコマンドと説明しています。--jsonで結果をJSONにでき、--strictは警告もエラー扱いにします(CI向け)。
skills/greet/SKILL.mdだけを置いた最小のディレクトリと、存在しないパスに実行した結果は次のとおりです(Claude Code v2.1.289で実行)。
$ claude plugin validate ./my-plugin
Validating components in: /path/to/my-plugin
✔ Validation passed
$ claude plugin validate ./nonexist
Validating plugin manifest: /path/to/nonexist
✘ Found 1 error:
❯ file: File not found: /path/to/nonexist
✘ Validation failedplugin.jsonが無いディレクトリでもSkillsだけで合格しました。マニフェストが任意という公式の説明と合う結果です。このコマンドはディレクトリの中身を見るもので、SDKのpluginsオプションに渡した相対パスがcwdからたどれるかまでは見ません。そちらはplugin_errorsのpathで確かめます。
よくあるつまずき
プラグインがinitメッセージのpluginsに出てこないときは、次の順に確認します。
plugins に出てこないとき
- 1
plugin_errors を読む
path-not-foundならパスの誤り、manifest-validation-errorならマニフェストの不備です(plugin_errorsはTypeScript版の場合。Pythonではinitのpluginsに出ているかで確かめます)。 - 2
パスがプラグインのルートか見る
skills/などの親ディレクトリ、つまりプラグインのルートを指している必要があります。ルート以外を渡すと、意図した中身が読み込まれません。 - 3
相対パスの基準を疑う
基準は
cwdオプションです。別のディレクトリから起動すると、書いたつもりのパスが解決できなくなります。 - 4
plugin.json と権限を見る
plugin.jsonを置いているならJSON構文を、ディレクトリには読み取り権限を確かめます。
プラグインのSkillsが動かない: /plugin-name:skill-nameの名前空間付きで呼び出しているか確認します。initメッセージのskills配列に正しい名前空間で並んでいるかも合わせて見ます。skills/my-skill/SKILL.mdのようにSkillごとにサブディレクトリを切っているかも確認点です。
Agent SDKのプラグインとCLIのプラグインの関係
同じプラグイン形式を、CLIとAgent SDKは別の入口から読み込みます。
プラグインの入口の違い
CLI
/pluginコマンドでインストールして使います。
Agent SDK
pluginsオプションでディレクトリを直接読み込みます。
プラグインが提供するSkills・Agents・Hooks・MCPサーバーの中身は共通で、CLI用に作ったプラグインをそのままAgent SDKから使えます。常駐アプリとしてSDKを組み込む実装例はAgent SDKでSlack常駐botを作るで扱っています。
よくある質問
pluginsオプションとskillsオプションは併用できますか
できます。skillsオプションにはプラグイン由来のSkillsをplugin:skillの形で指定します。名前は完全一致で渡す必要があり、skillsを指定するとSDKがSkillツールをallowedToolsに自動で加えます。
セッション中にプラグインを差し替えられますか
TypeScript版のクエリオブジェクトのreloadPlugins()が、ディスクからプラグインを読み直します。セッション中にインストールや編集したプラグインが、動いているセッションに反映されます。Agent SDK v0.2.85以降が必要で、結果にはコマンド・サブエージェント・プラグイン・MCPサーバーの状態が返ります。
事前に温めたプロセスでpluginsを変えられますか
prewarm()で作った待機プロセスでは、pluginsはmcpServersやhooksと同じく待機の間は固定です。pluginsの組み合わせごとに待機プロセスを分け、組み合わせが変わったら温め直します。
まとめ
読み込みを前提にした処理の前には、initメッセージのpluginsとplugin_errorsを見る確認を1か所入れておくと、パスの誤りによる読み飛ばしに早く気づけます。