Claude Agent SDKは、アプリケーション内でClaudeがツールを使用する方法を管理できる強力なパーミッション制御を提供します。
このガイドでは、canUseToolコールバック、フック、settings.jsonパーミッションルールを使用してパーミッションシステムを実装する方法について説明します。完全なAPI ドキュメントについては、TypeScript SDK リファレンスを参照してください。
Claude Agent SDKは、ツール使用を制御するための4つの相互補完的な方法を提供します:
各アプローチのユースケース:
canUseTool - カバーされていないケースの動的承認、ユーザーにパーミッションを求める処理順序: PreToolUse Hook → 拒否ルール → 許可ルール → 質問ルール → パーミッションモードチェック → canUseTool コールバック → PostToolUse Hook
パーミッションモードは、Claudeがツールを使用する方法に対するグローバル制御を提供します。query()を呼び出すときにパーミッションモードを設定するか、ストリーミングセッション中に動的に変更できます。
SDKは4つのパーミッションモードをサポートしており、それぞれ異なる動作があります:
| モード | 説明 | ツール動作 |
|---|---|---|
default | 標準的なパーミッション動作 | 通常のパーミッションチェックが適用されます |
plan | 計画モード - 実行なし | Claudeは読み取り専用ツールのみを使用できます。実行前に計画を提示します (現在SDKではサポートされていません) |
acceptEdits | ファイル編集の自動受け入れ | ファイル編集とファイルシステム操作は自動的に承認されます |
bypassPermissions | すべてのパーミッションチェックをバイパス | すべてのツールはパーミッションプロンプトなしで実行されます(注意して使用してください) |
パーミッションモードは2つの方法で設定できます:
クエリを作成するときにモードを設定します:
import { query } from "@anthropic-ai/claude-agent-sdk";
const result = await query({
prompt: "Help me refactor this code",
options: {
permissionMode: 'default' // Standard permission mode
}
});ストリーミングセッション中にモードを変更します:
acceptEdits)編集受け入れモードでは:
自動承認される操作:
bypassPermissions)パーミッションバイパスモードでは:
パーミッションモードはパーミッションフロー内の特定のポイントで評価されます:
bypassPermissions モード - アクティブな場合、残りのすべてのツールを許可しますcanUseTool コールバックに委譲しますcanUseTool コールバック - 残りのケースを処理しますこれは以下を意味します:
bypassPermissionsモードでもツール使用を制御できますbypassPermissionsモードはマッチしないツールのcanUseToolコールバックをオーバーライドしますモード進行の例:
// Start in default mode for controlled execution
permissionMode: 'default'
// Switch to acceptEdits for rapid iteration
await q.setPermissionMode('acceptEdits')canUseToolコールバックは、query関数を呼び出すときにオプションとして渡されます。ツール名と入力パラメータを受け取り、決定(許可または拒否)を返す必要があります。
canUseTool は、Claude Code がユーザーにパーミッションプロンプトを表示する場合に発火します。例えば、フックとパーミッションルールでカバーされておらず、acceptEdits モードではない場合です。
以下は、インタラクティブなツール承認を実装する方法を示す完全な例です:
AskUserQuestionツールを使用すると、Claude は会話中にユーザーに明確化の質問をすることができます。このツールが呼び出されると、canUseToolコールバックは質問を受け取り、ユーザーの回答を返す必要があります。
canUseToolがtoolName: "AskUserQuestion"で呼び出されると、入力には以下が含まれます:
{
questions: [
{
question: "Which database should we use?",
header: "Database",
options: [
{ label: "PostgreSQL", description: "Relational, ACID compliant" },
{ label: "MongoDB", description: "Document-based, flexible schema" }
],
multiSelect: false
},
{
question: "Which features should we enable?",
header: "Features",
options: [
{ label: "Authentication", description: "User login and sessions" },
{ label: "Logging", description: "Request and error logging" },
{ label: "Caching", description: "Redis-based response caching" }
],
multiSelect: true
}
]
}updatedInput.answersに回答を返します。これは質問テキストを選択されたオプションラベルにマッピングするレコードです:
return {
behavior: "allow",
updatedInput: {
questions: input.questions, // Pass through original questions
answers: {
"Which database should we use?": "PostgreSQL",
"Which features should we enable?": "Authentication, Caching"
}
}
}複数選択の回答はカンマ区切り文字列です(例:"Authentication, Caching")。
import { query } from "@anthropic-ai/claude-agent-sdk";
// Create an async generator for streaming input
async function* streamInput() {
yield {
type: 'user',
message: {
role: 'user',
content: "Let's start with default permissions"
}
};
// Later in the conversation...
yield {
type: 'user',
message: {
role: 'user',
content: "Now let's speed up development"
}
};
}
const q = query({
prompt: streamInput(),
options: {
permissionMode: 'default' // Start in default mode
}
});
// Change mode dynamically
await q.setPermissionMode('acceptEdits');
// Process messages
for await (const message of q) {
console.log(message);
}import { query } from "@anthropic-ai/claude-agent-sdk";
async function promptForToolApproval(toolName: string, input: any) {
console.log("\n🔧 Tool Request:");
console.log(` Tool: ${toolName}`);
// Display tool parameters
if (input && Object.keys(input).length > 0) {
console.log(" Parameters:");
for (const [key, value] of Object.entries(input)) {
let displayValue = value;
if (typeof value === 'string' && value.length > 100) {
displayValue = value.substring(0, 100) + "...";
} else if (typeof value === 'object') {
displayValue = JSON.stringify(value, null, 2);
}
console.log(` ${key}: ${displayValue}`);
}
}
// Get user approval (replace with your UI logic)
const approved = await getUserApproval();
if (approved) {
console.log(" ✅ Approved\n");
return {
behavior: "allow",
updatedInput: input
};
} else {
console.log(" ❌ Denied\n");
return {
behavior: "deny",
message: "User denied permission for this tool"
};
}
}
// Use the permission callback
const result = await query({
prompt: "Help me analyze this codebase",
options: {
canUseTool: async (toolName, input) => {
return promptForToolApproval(toolName, input);
}
}
});