Agent SDK でプラグインを読み込む設定と確認方法

Claude Agent SDK エンジニアリング 7分で読めます

プラグインは、スキル・エージェント・フック・MCP サーバーを1つのディレクトリにまとめて配布できる仕組みです。Claude Agent SDK では plugins オプションにローカルのパスを渡すことで、プロジェクトをまたいで同じ機能をコードから読み込めます。

このページでは、Agent SDK からプラグインを読み込む書き方(TypeScript / Python)、読み込めたことを確かめる方法、プラグインのスキルを名前空間つきで呼ぶ形、そして読み込まれないときに見る順番までを扱います。プラグイン自体の作り方ではなく、SDK 側からどう読ませるかが主題です。

プラグインで追加できる4種類

1つのプラグインには、次の4種類を入れられます。

ディレクトリの構成は次のとおりです。.claude-plugin/plugin.json のマニフェストは任意で、省略した場合は Claude Code がディレクトリ構成から各コンポーネントを自動検出します。

my-plugin/
├── .claude-plugin/
│   └── plugin.json          # マニフェスト(任意。無い場合は自動検出)
├── skills/                  # スキル(自律呼び出し//plugin-name:skill-name)
│   └── my-skill/
│       └── SKILL.md
├── commands/                # フラットな .md ファイルとしてのスキル
│   └── custom-cmd.md
├── agents/                  # カスタムエージェント
│   └── specialist.md
├── hooks/                   # イベントハンドラ
│   └── hooks.json
└── .mcp.json                # MCP サーバー定義

commands/ はフラットな Markdown ファイルとしてスキルを置く場所です。新しいプラグインでは skills/ を使ってください。Claude Code は両方の配置に対応しています。

plugins オプションで読み込む

読み込みは、オプション設定にローカルのファイルシステムパスを渡すだけです。type フィールドは "local" でなければならず、これが SDK の受け付ける唯一の値です。異なる場所から複数のプラグインを読み込めます。

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Hello",
  options: {
    plugins: [
      { type: "local", path: "./my-plugin" },
      { type: "local", path: "/absolute/path/to/another-plugin" }
    ]
  }
})) {
  // プラグインのコマンド・エージェントなどが利用可能になる
}

Python では ClaudeAgentOptions に同じ形で渡します。

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions


async def main():
    async for message in query(
        prompt="Hello",
        options=ClaudeAgentOptions(
            plugins=[
                {"type": "local", "path": "./my-plugin"},
                {"type": "local", "path": "/absolute/path/to/another-plugin"},
            ]
        ),
    ):
        pass


asyncio.run(main())

パスの指定には2通りあります。相対パス(例 "./plugins/my-plugin")はカレントワーキングディレクトリからの解決になり、絶対パス(例 "/home/user/plugins/my-plugin")はそのままのフルパスです。指定するのはプラグインのルートディレクトリ、つまり skills/agents/hooks/commands/.claude-plugin/ の親にあたる階層です。

ここで2点、実務上つまずきやすい仕様があります。

複数の場所から組み合わせるときは次のように書きます。

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")
  }
];

マーケットプレイスやリモートリポジトリで配布されているプラグインを使う場合は、先にダウンロードしてローカルのディレクトリパスを渡します。また、CLI で /plugin install my-plugin@marketplace のようにインストールしたプラグインも、そのインストール先パスを渡せば SDK から使えます。CLI でインストールしたものは ~/.claude/plugins/ を確認してください。

init メッセージで読み込みを確認する

プラグインの読み込みに成功すると、システム初期化メッセージに現れます。パスの指定ミスは例外にならないため、ここを見るのが唯一の確認手段です。

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Hello",
  options: {
    plugins: [{ type: "local", path: "./my-plugin" }]
  }
})) {
  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"]
  }
}

Python では SystemMessagesubtype"init" のときに message.data.get("plugins") などで同じ内容を取り出します。

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage


async def main():
    async for message in query(
        prompt="Hello",
        options=ClaudeAgentOptions(
            plugins=[{"type": "local", "path": "./my-plugin"}]
        ),
    ):
        if isinstance(message, SystemMessage) and message.subtype == "init":
            print("Plugins:", message.data.get("plugins"))
            print("Skills:", message.data.get("skills"))
            print("Commands:", message.data.get("slash_commands"))


asyncio.run(main())

見るべきは3つのリストです。plugins には読み込まれたプラグインの名前と絶対パスが並びます。skills には名前空間つきのスキル名が、slash_commands には組み込みコマンド(compactcontext など)とプラグイン由来のコマンドおよびスキルが並びます。渡したはずのプラグインが plugins に無ければ、パスが解決できていません。

プラグインのスキルは名前空間つきで呼ぶ

プラグインのスキルは、衝突を避けるためにプラグイン名で自動的に名前空間が付きます。直接呼び出すには、プロンプトとして /plugin-name:skill-name を送ります。

import { query } from "@anthropic-ai/claude-agent-sdk";

// カスタムの /greet スキルを持つプラグインを読み込む
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);
  }
}

Python でも同じく、prompt="/my-plugin:greet" のように名前空間つきの文字列をプロンプトとして渡します。

接頭辞を省いて /greet と書いても、プラグインのスキルには届きません。これは複数のプラグインが同じスキル名を持ちうるための設計です。init メッセージの skills に出ている文字列を、そのままプロンプトに使うのが確実です。

読み込まれないときに見る順番

公式ドキュメントは、症状ごとに確認する順番を示しています。

プラグインが init メッセージに現れない場合は次の順で見ます。

  1. パスを確認するskills/agents/hooks/commands/.claude-plugin/ の親であるプラグインのルートディレクトリを指しているか。
  2. plugin.json を検証する — マニフェストを含んでいる場合、JSON の構文が正しいか。
  3. ファイルの権限を確認する — プラグインのディレクトリが読み取り可能か。
  4. ディレクトリの存在を確認する — 存在しないパスは SDK がスキップし、init メッセージの plugins リストに現れない。

プラグインのスキルが動かない場合は次の順です。

  1. 名前空間を使う/plugin-name:skill-name の形で呼ぶ。
  2. init メッセージを確認するskills リストに正しい名前空間で出ているか。
  3. スキルのファイルを検証する — 各スキルが skills/ 配下の自分のサブディレクトリに SKILL.md を持っているか(例 skills/my-skill/SKILL.md)。

共通するのは、推測せず init メッセージの実際の値を見るという順序です。読み込み失敗が例外にならない仕様である以上、コードが動いていることは何の証拠にもなりません。

プラグイン自体の作り方と技術仕様については、公式のプラグイン開発ガイドプラグインリファレンスを参照してください。本ページは Plugins in the SDK の内容に基づく非公式の日本語解説です(確認日 2026-09-02)。