Bỏ qua để đến nội dung

Plugin trong SDK

Bài viết được dịch tự động từ bài viết gốc, chưa được kiểm tra lại bởi con người. Chỉ những bài viết có dấu tick xanh cạnh tiêu đề là đã được kiểm tra.

Plugin cho phép bạn mở rộng Claude Code với chức năng tùy chỉnh có thể chia sẻ qua nhiều dự án. Qua Agent SDK, bạn có thể load plugin theo cách lập trình từ thư mục local để thêm skill, agent, hook, và MCP server vào agent session của mình.

Plugin là các gói mở rộng Claude Code có thể gồm:

  • Skills: năng lực do model gọi mà Claude dùng tự động (cũng có thể gọi bằng /skill-name)
  • Agents: subagent chuyên biệt cho tác vụ cụ thể
  • Hooks: trình xử lý sự kiện phản hồi việc dùng tool và các sự kiện khác
  • MCP servers: tích hợp tool bên ngoài qua Model Context Protocol

Để biết thông tin đầy đủ về cấu trúc plugin và cách tạo plugin, xem Plugins.

Load plugin bằng cách cung cấp đường dẫn filesystem local của chúng trong cấu hình tùy chọn. Trường type phải là "local", giá trị duy nhất SDK chấp nhận. SDK hỗ trợ load nhiều plugin từ các vị trí khác nhau.

Để dùng một plugin phân phối qua marketplace hay repository từ xa, hãy tải nó về trước và cung cấp đường dẫn thư mục local. Về cấu trúc thư mục mà một plugin cần, xem Tài liệu tham khảo cấu trúc plugin bên dưới.

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" }
]
}
})) {
// Command, agent, và các tính năng khác của plugin giờ đã khả dụng
}
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"},
]
),
):
# Command, agent, và các tính năng khác của plugin giờ đã khả dụng
pass
asyncio.run(main())

Đường dẫn plugin có thể là:

  • Đường dẫn tương đối: được resolve tương đối với thư mục làm việc hiện tại của bạn (ví dụ: "./plugins/my-plugin")
  • Đường dẫn tuyệt đối: đường dẫn filesystem đầy đủ (ví dụ: "/home/user/plugins/my-plugin")

Khi plugin load thành công, chúng xuất hiện trong system initialization message. Bạn có thể xác minh plugin của mình khả dụng:

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") {
// Kiểm tra plugin đã load
console.log("Plugins:", message.plugins);
// Ví dụ: [{ name: "my-plugin", path: "/absolute/path/to/my-plugin" }]
// Skill của plugin xuất hiện với tiền tố tên plugin
console.log("Skills:", message.skills);
// Ví dụ: ["my-plugin:greet"]
// Command của plugin dùng cùng tiền tố, và skill cũng xuất hiện ở đây
console.log("Commands:", message.slash_commands);
// Ví dụ: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
}
}
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":
# Kiểm tra plugin đã load
print("Plugins:", message.data.get("plugins"))
# Ví dụ: [{"name": "my-plugin", "path": "/absolute/path/to/my-plugin"}]
# Skill của plugin xuất hiện với tiền tố tên plugin
print("Skills:", message.data.get("skills"))
# Ví dụ: ["my-plugin:greet"]
# Command của plugin dùng cùng tiền tố, và skill cũng xuất hiện ở đây
print("Commands:", message.data.get("slash_commands"))
# Ví dụ: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
asyncio.run(main())

Skill từ plugin tự động được đặt namespace theo tên plugin để tránh xung đột. Để gọi trực tiếp một skill, gửi /plugin-name:skill-name làm prompt.

import { query } from "@anthropic-ai/claude-agent-sdk";
// Load một plugin có skill /greet tùy chỉnh
for await (const message of query({
prompt: "/my-plugin:greet", // Dùng skill của plugin kèm namespace
options: {
plugins: [{ type: "local", path: "./my-plugin" }]
}
})) {
// Claude thực thi skill chào hỏi tùy chỉnh từ plugin
if (message.type === "assistant") {
console.log(message.message.content);
}
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock
async def main():
# Load một plugin có skill /greet tùy chỉnh
async for message in query(
prompt="/my-plugin:greet", # Dùng skill của plugin kèm namespace
options=ClaudeAgentOptions(
plugins=[{"type": "local", "path": "./my-plugin"}]
),
):
# Claude thực thi skill chào hỏi tùy chỉnh từ plugin
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
asyncio.run(main())

Đây là một ví dụ đầy đủ minh hoạ việc load và dùng plugin:

import { query } from "@anthropic-ai/claude-agent-sdk";
import { fileURLToPath } from "node:url";
async function runWithPlugin() {
const pluginPath = fileURLToPath(new URL("./plugins/my-plugin", import.meta.url));
console.log("Loading plugin from:", pluginPath);
for await (const message of query({
prompt: "What custom commands do you have available?",
options: {
plugins: [{ type: "local", path: pluginPath }],
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);
}
}
}
runWithPlugin().catch(console.error);
#!/usr/bin/env python3
"""Ví dụ minh hoạ cách dùng plugin với Agent SDK."""
import asyncio
from pathlib import Path
from claude_agent_sdk import (
AssistantMessage,
ClaudeAgentOptions,
SystemMessage,
TextBlock,
query,
)
async def run_with_plugin():
"""Ví dụ dùng một plugin tùy chỉnh."""
plugin_path = Path(__file__).parent / "plugins" / "my-plugin"
print(f"Loading plugin from: {plugin_path}")
options = ClaudeAgentOptions(
plugins=[{"type": "local", "path": str(plugin_path)}],
max_turns=3,
)
async for message in query(
prompt="What custom commands do you have available?", options=options
):
if isinstance(message, SystemMessage) and message.subtype == "init":
print(f"Loaded plugins: {message.data.get('plugins')}")
print(f"Available skills: {message.data.get('skills')}")
print(f"Available commands: {message.data.get('slash_commands')}")
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Assistant: {block.text}")
if __name__ == "__main__":
asyncio.run(run_with_plugin())

Một thư mục plugin thường chứa một file manifest .claude-plugin/plugin.json. Manifest là tùy chọn. Khi bỏ trống, Claude Code tự phát hiện các thành phần từ cấu trúc thư mục. Thư mục có thể gồm:

my-plugin/
├── .claude-plugin/
│ └── plugin.json # Manifest plugin (tùy chọn, thành phần được tự phát hiện nếu không có)
├── skills/ # Agent Skills (gọi tự động hoặc qua /skill-name)
│ └── my-skill/
│ └── SKILL.md
├── commands/ # Legacy: dùng skills/ thay vào đó
│ └── custom-cmd.md
├── agents/ # Agent tùy chỉnh
│ └── specialist.md
├── hooks/ # Trình xử lý sự kiện
│ └── hooks.json
└── .mcp.json # Định nghĩa MCP server

Để biết thông tin chi tiết về tạo plugin, xem:

Load plugin trong lúc phát triển mà không cần cài đặt toàn cục:

plugins: [{ type: "local", path: "./dev-plugins/my-plugin" }];

Đưa plugin vào repository dự án của bạn để nhất quán trong toàn team:

plugins: [{ type: "local", path: "./project-plugins/team-workflows" }];

Kết hợp plugin từ các vị trí khác nhau:

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

Nếu plugin của bạn không xuất hiện trong init message:

  1. Kiểm tra đường dẫn: đảm bảo đường dẫn trỏ tới thư mục gốc của plugin, thư mục cha của skills/, agents/, hooks/, commands/ (legacy), hay .claude-plugin/
  2. Xác thực plugin.json: nếu plugin của bạn gồm một manifest, đảm bảo nó có cú pháp JSON hợp lệ
  3. Kiểm tra quyền file: đảm bảo thư mục plugin có thể đọc được
  4. Xác nhận thư mục tồn tại: SDK bỏ qua một đường dẫn không tồn tại, và plugin sẽ không xuất hiện trong danh sách plugins của init message

Nếu skill của plugin không hoạt động:

  1. Dùng namespace: gọi skill của plugin dưới dạng /plugin-name:skill-name
  2. Kiểm tra init message: xác minh skill xuất hiện trong danh sách skills với namespace đúng
  3. Xác thực file skill: đảm bảo mỗi skill có một file SKILL.md trong thư mục con riêng dưới skills/, ví dụ skills/my-skill/SKILL.md

Nếu đường dẫn tương đối không hoạt động:

  1. Kiểm tra thư mục làm việc: đường dẫn tương đối được resolve từ thư mục làm việc hiện tại của bạn
  2. Dùng đường dẫn tuyệt đối: để đáng tin cậy hơn, cân nhắc dùng đường dẫn tuyệt đối
  3. Chuẩn hoá đường dẫn: dùng path utility để dựng đường dẫn đúng cách