1
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?

Claude Code サブエージェントを作って、tools: で権限を絞るまでの手順(実機で確認)

1
Posted at

Claude Code のサブエージェントは、.claude/agents/ に Markdown を1枚置くだけで作れます。ただ、定義の本文に「読み取り専用です。ファイルを変更してはいけません」と書いても、それは権限の設定ではありません。 使えるツールを決めるのは、先頭の frontmatter の tools: です。

自分はサブエージェントを10体以上、定義ファイルで運用しています。その中に、定義の説明文に「読み取り専用」と書いてあるのに、Bash を持っているので echo > file や git commit ができる状態の定義があります。分析のスクリプトを動かすために Bash が要るからで、今も残しています。本文の「読み取り専用」は「お願い」でしかなく、ツールで強制できていません。

この記事は、最初から1体作って、次の4点を実機で確かめる手順です。

  1. 定義を置いて、呼び出せることを確認する
  2. tools: を書かないと、書き込みも Bash も使えてしまうのを見る
  3. tools: Read, Grep に絞ると、Write も Bash も渡されないのを見る
  4. 詰まりどころ(disallowedTools の落とし穴、ツール名のタイプミス)

動作確認した環境

  • Claude Code 2.1.317(2026-11-07 に実機確認)
  • macOS / zsh(Windows・Linux では確かめていません)
  • 検証は使い捨ての git リポジトリで、claude -p(ヘッドレスモード)から行いました。以降のパスは <テスト用ディレクトリ> と書きます

手順1: 定義ファイルを置く

プロジェクト専用のサブエージェントは .claude/agents/ に置きます。公式ドキュメントによると、置き場所は優先度の高い順に次のとおりです。

優先度 置き場所 範囲
1 組織の管理設定 組織全体
2 --agents CLI フラグ そのセッションだけ
3 .claude/agents/ そのプロジェクト
4 ~/.claude/agents/ 自分の全プロジェクト
5 プラグインの agents/ プラグインを有効にした範囲

同じ name が複数あれば、優先度の高いほうが使われます。必須の frontmatter は name と description の2つだけです。description は、Claude が「いつこのエージェントに任せるか」を決める文なので、用途を具体的に書きます。

テスト用のリポジトリを作って、最初の定義を置きます。ここでは、あえて本文に「読み取り専用」と書き、tools: は書かない版にします。

mkdir -p <テスト用ディレクトリ>/.claude/agents && cd <テスト用ディレクトリ>
git init -q && echo "hello" > sample.txt

.claude/agents/reviewer.md

---
name: reviewer
description: コードを読んで指摘だけする担当。sample.txt などの確認を頼まれたときに使う。
---

あなたはレビュー担当です。**読み取り専用**です。ファイルを変更してはいけません。指摘を返すだけにしてください。

公式ドキュメントによると、~/.claude/agents/ と .claude/agents/ は Claude Code が監視していて、ファイルを足したり編集したりすれば数秒で反映され、再起動は要りません。例外は、その範囲で最初のエージェントファイルを新しい agents ディレクトリに作ったときなどで、再起動が必要です。今回は定義を置いてから claude -p を起動しているので、この問題には当たりませんでした。

手順2: 呼び出せることを確認する

名前を指定して頼めば、Claude が委譲します。公式ドキュメントでは、ほかに @agent-<名前> で明示的に指定する方法と、claude --agent <名前> でセッション全体をそのエージェントとして動かす方法も紹介されています(この2つは今回は試していません)。

claude -p "reviewer エージェントに委譲して、sample.txt の中身を読んで1行で報告させて。あなた自身は読まず、必ず reviewer に任せて。最後に、使ったエージェント名を書いて" \
  --allowedTools "Agent,Read" < /dev/null

返ってきたのはこうでした。

reviewer の報告では、`sample.txt` の中身は「hello」の1行だけです(ファイルは変更していません)。
使ったエージェント名: **reviewer**

--output-format stream-json --verbose を付けると、メインが呼んだツールの記録が JSON で出ます。jq で見ると、Agent ツールが subagent_type: "reviewer" で呼ばれていました。これで「呼び出せた」と言えます。

手順3: tools を書かないと、何が使えるか

まず、本文に「読み取り専用」と書いた reviewer に、わざと書き込みを頼みました。

claude -p "reviewer エージェントに委譲して、sample.txt を読み、指摘を report.txt に保存させて(保存は Write ツールか Bash のどちらでもよい)。reviewer が実際に呼んだツール名を報告させて。" \
  --allowedTools "Agent,Write,Bash,Read" --output-format stream-json --verbose < /dev/null

結果は、report.txt は作られませんでした。 reviewer は Read を1回呼んだだけで、保存は「読み取り専用と書かれているから」と自分で断っています。Write や Bash の許可が無かったからではありません。許可は渡していました。同じ条件で Write と Bash に直接書き込みを頼む版も試しましたが、同じく断られました(2回とも)。

ここで「本文に書けば守られる」と思ってはいけません。断ったのはモデルが文章を守ったからで、仕組みで止めたわけではないからです。確かめるために、本文に書き込みを禁じる文が無い版を作ります。tools: は書きません。

.claude/agents/notes-open.md

---
name: notes-open
description: ファイルを読んで要点をまとめる担当。tools を指定していない版。
---

あなたは要点まとめ担当です。頼まれたら、ファイルを読んで結果を返します。
claude -p "notes-open エージェントに委譲して、自分が使えるツール名を全部列挙させて。その後、Write ツールで written-open.txt に「open」と書かせて。あなた自身は書かない。結果をそのまま報告して。" \
  --allowedTools "Agent,Write,Bash,Read" < /dev/null

結果は次のとおりです。

  • ツールの列挙: Bash Edit Read Write Skill ToolSearch など、メインに近い一式に加えて、自分の環境に接続している MCP サーバーのツール(外部サービスの API を叩くもの)まで入っていました(MCP を接続していない環境なら、この部分は出ません)
  • written-open.txt が作られ、中身は open でした(メインは書いていません。エージェントが Write で書いています)

公式ドキュメントの記述どおりです。

Inherits every tool available to subagents if omitted.(tools を省略すると、サブエージェントが使えるツールをすべて継承する)

つまり、tools: を書かない定義は「全部使える」定義です。本文に何と書いてあっても、この状態は変わりません。

手順4: tools: Read, Grep に絞る

同じ定義に tools: を1行足します。

.claude/agents/notes-ro.md

---
name: notes-ro
description: ファイルを読んで要点をまとめる担当。tools を Read, Grep に絞った版。
tools: Read, Grep
---

あなたは要点まとめ担当です。頼まれたら、ファイルを読んで結果を返します。
claude -p "notes-ro エージェントに委譲して、(a) 自分が使えるツール名を、MCP 以外すべて列挙させて(MCP は個数だけ)。(b) Write ツールで written-ro.txt に「ro」と書こうとさせる (c) Bash ツールで echo b > written-ro-bash.txt を実行しようとさせる。各結果を全文そのまま報告させて。" \
  --allowedTools "Agent,Write,Bash,Read" < /dev/null

結果です。

  • 使えるツール: Read と Grep の2個。MCP ツールは0個
  • Write も Bash も、ツール一覧に無い(呼び出せない)
  • written-ro.txt も written-ro-bash.txt も作られていない

ここで注意してほしいのは、「実行したら拒否された」ではないことです。エラーメッセージはありません。そのツールが最初から渡されていないので、エージェントは呼ぶことができません。権限のダイアログで止めるのではなく、手札から外す仕組みです。メイン側に --allowedTools "Write,Bash" を渡していても、結果は変わりませんでした。

これが「tools: は許可リスト」という意味です。公式ドキュメントでも、ツールの制限には tools(許可リスト)か disallowedTools(拒否リスト)を使うと書かれています。

詰まりどころ

disallowedTools: Write, Edit だけでは、Bash から書ける

「書き込みを止めたい」だけなら、disallowedTools: Write, Edit で足りそうに見えます。試しました。

---
name: notes-deny
description: ファイルを読んで要点をまとめる担当。disallowedTools で Write, Edit だけ外した版。
disallowedTools: Write, Edit
---

結果は、Write は使えず、Bash は使えて、echo b > written-deny-bash.txt でファイルが作られました。 disallowedTools は、継承したツールの一式から指定したものだけを引く仕組みなので、Bash などは残ります。公式ドキュメントにも、同じ disallowedTools: Write, Edit で「書き込み系を除いて継承する」例(no-writes)が載っています(Bash が残ることは、上の実機で確かめました)。

読み取り専用にしたいなら、拒否リストで引くより、tools: Read, Grep, Glob のように許可リストで必要なものだけを書くほうが、取りこぼしが出ません。両方書いた場合は、disallowedTools が先に適用され、残ったものに対して tools が解決され、両方に書いたツールは外れる、と公式ドキュメントにあります。

tools のツール名を間違えると、起動を拒否される

tools: Reed, Grap と、ツール名をわざと間違えた定義を呼んでみました。

Agent 'notes-typo' would be spawned with zero tools — refusing. Its tools list resolved to nothing: unrecognized [Reed, Grap]. Fix the agent's tools frontmatter or pass a different subagent_type.

ツールが1つも解決できないときは、起動自体が拒否されます。「間違えたせいで権限が無制限になる」ことはなく、安全側に倒れます。ただし、一部だけ間違えたときの挙動は試していません。公式ドキュメントも「たいていは拒否する(usually refuses)」という書き方です。

Bash を入れた時点で、読み取り専用ではない

調査用のエージェントに、git log やスクリプトの実行のために Bash を入れることはよくあります。ただし Bash があると、リダイレクトで書けますし、git commit もできます。Bash を渡すなら、そのエージェントは「読み取り専用」ではありません。本文の「読み取り専用」は、あくまでお願いです。自分の運用でも、定義の文章は「読み取り専用」なのに Bash を持っているものがあり、今もそこはツールで強制できていません。

Bash を渡して書き込みだけ止めたい場合は、tools: ではなくフックなど別の仕組みが要ります。この記事では扱いません(フックで止める手順は1本目の記事にあります。止まる範囲と、すり抜ける穴も書いています)。

自分が使っている形(縮約)

運用では、書き込む必要のあるエージェントだけが Write / Edit を持ち、調べるだけのエージェントは Read と Grep 中心にしています。形はこれだけです。

---
name: researcher
description: 実データを読んで分析する担当。調べるだけで、変更はしない。
tools: Read, Grep, Glob, WebFetch
model: sonnet
---

(役割・見るもの・出力の形を本文に書く)

model: で sonnet / opus / haiku などを指定できます。本文は役割と手順、tools: は権限、と役割を分けて書くと、あとで見直しやすくなります。

後片付け

テスト用ディレクトリは、検証が終わったら消します。~/.claude/agents/ に置いた自分の定義は、この手順では触っていません。

まとめ

  • サブエージェントは .claude/agents/<名前>.md。必須は name と description
  • tools: を省略すると、全ツール(MCP ツールを含む)を継承する。本文に「読み取り専用」と書いても、それはお願いでしかない
  • tools: Read, Grep と許可リストで絞ると、Write と Bash は手札に渡されない(拒否ではなく、そもそも無い)
  • disallowedTools: Write, Edit だけでは、Bash から書ける。読み取り専用にしたいなら許可リスト
  • ツール名のタイプミスで解決できる名前が0になると、起動を拒否される

関連:

1
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
1
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?