AIDive

Paket video

Skill, hook, subagent, atau MCP: 4 file dan kartu pemilih

7 menit baca

TL;DR dan putusan

Empat tugas yang terus kamu ketik ke Claude Code masing-masing punya file yang berbeda. Checklist rilis adalah skill. Aturan yang tidak boleh dilanggar adalah hook PreToolUse. Audit yang membaca banyak file adalah subagent. Data yang tidak bisa dilihat Claude adalah server MCP.

Kami menjalankan keempatnya di satu repo nyata, sebuah API invoice dengan 34 tes, dan setiap kali membuat pilihan yang salah dulu. Pilihan yang salah itu gagal di layar:

  • Checklist di CLAUDE.md menambah baris Memory files: 210 tokens ke /context, di setiap giliran.
  • Hook pertama jalan, mencetak peringatannya, dan penulisan lolos (unitPrice 8000 jadi 12000).
  • Audit yang dijalankan inline memenuhi thread: Messages di 19.3k token, dibanding 7.2k dengan subagent yang disebut namanya.
  • Tanpa server docs, Claude menjawab "I couldn't find that setting... I won't guess one."

Putusan: pilih jenisnya dari tabel docs, lalu buat file itu menjawab, bukan sekadar ada. Hook yang hanya mencatat log bersifat saran saja, seperti baris CLAUDE.md yang digantikannya. Dari keempatnya, hanya hook yang tidak pernah bertanya.

Salin file di bawah, commit, dan mulai dari yang paling sering kamu ketik ulang.

Sintesis bersumber

Apa yang dimuat sesimu sebelum kata pertamamu. /context baseline kami menunjukkan 26.2k/1m token, tanpa baris memory dan tanpa baris MCP. Setiap tugas yang kamu tambahkan akan menaruh satu baris di tabel itu atau tidak. Baca dulu, lalu ukur terhadapnya.

Aturan CLAUDE.md adalah permintaan, hook adalah penegakan. Docs menyebut instruksi seperti "never edit .env" di CLAUDE.md atau skill sebagai permintaan, dan hook PreToolUse yang memblokir edit sebagai penegakan s1. Halaman Memory menyebut CLAUDE.md sebagai context, bukan penegakan s8. Sebuah thread di Hacker News melaporkan hal yang sama dari sisi hook: hook yang keluar dengan 0 tidak memblokir apa pun s10.

Tabel pemicu memberi tahu file mana. Prosedur yang ditempel untuk ketiga kalinya berarti skill. Output yang membanjiri chat berarti subagent. Tab yang tidak terlihat Claude berarti server MCP. Sesuatu yang harus terjadi setiap kali berarti hook s1.

Tugas 1, checklist rilis, adalah skill.

  • Prosedur yang tumbuh di dalam CLAUDE.md tempatnya di skill, dan isinya dimuat hanya saat dipakai s3.
  • Pada run kami, checklist yang ditempel ke CLAUDE.md menambah baris Memory files: 210 tokens. Sebagai skill dengan disable-model-invocation: true, /context kembali ke baseline.
  • /release menjalankan enam langkah (34 tes, 0.4.0 ke 0.4.1, tag v0.4.1). Ia juga menangkap bahwa git commit -am melewatkan file yang belum dilacak, jadi ia men-stage changelog baru lebih dulu.

Tugas 2, aturan yang harus tegak, adalah hook, dan jaminannya adalah jawaban yang dikembalikannya.

  • Exit 0 tanpa output bukan keputusan. Exit 2 memblokir. JSON deny juga memblokir s2.
  • Hook pertama kami mencatat log, exit 0, dan penulisan lolos (unitPrice 8000 jadi 12000). Balasan Claude tidak pernah menyebut hook itu.
  • Hook yang sama dengan mengembalikan JSON deny menolak edit dengan PreToolUse:Edit hook error: Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions). Lalu Claude bertanya: override, atau tulis migrasinya.
  • Docs menerbitkan resep PreToolUse untuk file yang dilindungi s23.
  • Batasnya: hook ini memantau Edit dan Write. Cakup shell juga, atau jauhkan shell dari daftar allow-mu.
  • onFailure: "block" membuat hook yang gagal memblokir, bukan gagal terbuka. Ini muncul di v2.1.295 s2 s15.

Tugas 3, audit yang membanjiri, adalah subagent.

  • Subagent mulai tanpa konteks dan mengembalikan ringkasan s4.
  • Inline, Messages terbaca 19.3k. Dengan subagent yang disebut namanya, 7.2k.
  • File agent saja mendelegasikan 0 dari 2 run. Menyebut namanya di prompt membuatnya mendelegasikan. File membuatnya ada, prompt-mu membuat Claude memakainya.
  • Pin model: di file. Tagihannya tidak bergerak: pembacaan tetap terjadi, hanya tidak di window-mu.

Tugas 4, data yang tak terjangkau Claude, adalah server MCP.

  • claude mcp add --scope project menulis .mcp.json, dan tool search hanya memuat nama tool s5.
  • Ada server HTTP gratis tanpa login untuk docs Claude Code sendiri, dengan search_claude_code_docs dan query_docs_filesystem_claude_code_docs s24.
  • Tanpanya, Claude menjawab "I couldn't find that setting... I won't guess one." Dengannya, Claude menjawab "onFailure": "block", ditambahkan di v2.1.295, sementara instalasi kami 2.1.294.
  • Server docs untuk library yang sudah diketahui model tidak mengubah apa pun bagi kami. Hubungkan yang tidak bisa dimiliki model.

Laporan lapangan. Sebuah thread Reddit melaporkan Claude mengabaikan baris "MUST use agent" di CLAUDE.md s9. Kami mengutipnya sebagai laporan, bukan pengukuran. Tulisan engineering Anthropic membahas pembagian yang sama s6 s7.

Code

Empat file dari run ini, ditambah dua pembantu. Path adalah tempat masing-masing berada di repo-mu.

1. Skill rilis, di .claude/skills/release/SKILL.md. Ketik /release. disable-model-invocation: true membuatnya hanya kamu yang memulai, dan itu yang kamu mau untuk sesuatu yang commit dan tag.

SKILL.md

---
name: release
description: Cut a patch release of ledgerly (typecheck, tests, version bump, changelog line, commit, tag).
disable-model-invocation: true
---

Run these steps in order and stop at the first failure:

1. `pnpm typecheck` must exit 0.
2. `pnpm test` must pass.
3. Bump the patch version in `package.json` (e.g. 0.4.0 -> 0.4.1).
4. Add one line at the top of `CHANGELOG.md` (create it if missing): `## vX.Y.Z - YYYY-MM-DD` followed by a one-line summary of the commits since the last tag.
5. `git commit -am "release vX.Y.Z"`
6. `git tag vX.Y.Z`. Never push.

SKILL.md on GitHub

2. Hook protect-data, di .claude/settings.json. Ia mengembalikan keputusan deny dengan alasan yang dibaca Claude. Ia butuh jq di PATH-mu dan muat inline, jadi hook tidak butuh file script sendiri. Ganti pola data/.*\.json$ dan alasannya sesuai aturanmu.

settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c 'if (.tool_input.file_path // \"\") | test(\"data/.*\\\\.json$\") then {hookSpecificOutput: {hookEventName: \"PreToolUse\", permissionDecision: \"deny\", permissionDecisionReason: \"Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions).\"}} else empty end'"
          }
        ]
      }
    ]
  }
}

settings.json on GitHub

3. Conventions auditor, di .claude/agents/conventions-auditor.md. Tool dibatasi ke yang hanya-baca dan modelnya di-pin. Edit daftar konvensi agar cocok dengan README-mu.

conventions-auditor.md

---
name: conventions-auditor
description: Use when asked to audit the code against the README conventions
model: sonnet
tools: Read, Grep, Glob
---

You audit ledgerly's source against README § Conventions:

- Money is always an integer number of cents. Never floats.
- Domain functions return new objects; they never mutate their input.
- Validation failures throw `ValidationError` (400), missing records `NotFoundError` (404), illegal state transitions `ConflictError` (409).
- A change of stored shape bumps `schemaVersion` and adds a migration in `src/store/migrations.ts`.

Read every file under `src/`. Return only the findings: one line per violation as `file:line - convention - what is wrong`, then borderline cases the same way, then one line with the count of files read. No file contents, no summary of clean files.

conventions-auditor.md on GitHub

4. Config MCP, disimpan sebagai .mcp.json di root repo. Inilah yang ditulis claude mcp add --transport http claude-docs --scope project https://code.claude.com/docs/mcp. Claude Code mencantumkan server project sebagai pending sampai kamu menyetujuinya di sesi berikutnya.

mcp.json

{
  "mcpServers": {
    "claude-docs": {
      "type": "http",
      "url": "https://code.claude.com/docs/mcp"
    }
  }
}

mcp.json on GitHub

Pembantu. ctx.sh menjalankan /context di sesi headless dan mencetak baris token, supaya kamu bisa membandingkan sebelum dan sesudah. mcp-probe.sh mengirim initialize dan tools/list ke url MCP HTTP, untuk memeriksa server menjawab sebelum kamu menambahkannya.

ctx.sh

#!/usr/bin/env bash
# Usage: ctx.sh <step>  -> runs "/context" through run-session.sh, writes context-<step>.txt (honours STRICT_MCP / EXTRA_ALLOW)
C="$(cd "$(dirname "$0")" && pwd)"
"$C/run-session.sh" "$C/out/context-$1.json" "/context" >/dev/null
python3 -c "import json,sys;d=json.load(open(sys.argv[1]));print(d['result'])" "$C/out/context-$1.json" > "$C/context-$1.txt"
grep -E '^\*\*Tokens|^\| (System prompt|System tools|MCP tools|Memory files|Skills|Custom agents|Messages) ' "$C/context-$1.txt"

ctx.sh on GitHub

mcp-probe.sh

#!/usr/bin/env bash
# Usage: mcp-probe.sh <url> [tools/call json params]  -> initialize + notifications/initialized + tools/list (+ optional tools/call) over streamable HTTP
U="$1"; H=$(mktemp)
curl -s -D "$H" -m 30 -X POST "$U" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}'
echo; SID=$(grep -i '^mcp-session-id:' "$H" | tr -d '\r' | cut -d' ' -f2); echo "session-id: ${SID:-none}"
HS=(-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream'); [ -n "$SID" ] && HS+=(-H "Mcp-Session-Id: $SID")
curl -s -m 30 -X POST "$U" "${HS[@]}" -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' >/dev/null
curl -s -m 30 -X POST "$U" "${HS[@]}" -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'; echo
[ -n "${2:-}" ] && { curl -s -m 90 -X POST "$U" "${HS[@]}" -d "{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":$2}"; echo; }
rm -f "$H"

mcp-probe.sh on GitHub

Lakukan ini hari Senin

  • Buka sesi baru dan jalankan /context. Catat totalnya. Itu baseline-mu.
  • Pilih checklist yang paling sering kamu tempel. Pindahkan ke .claude/skills/<name>/SKILL.md, tambahkan disable-model-invocation: true jika ia commit atau tag, dan jalankan dengan /<name>.
  • Ambil satu aturan yang tidak boleh dilanggar dan taruh di hook PreToolUse yang mengembalikan permissionDecision: "deny" atau exit 2. Minta Claude melanggar aturan itu, dan periksa bahwa edit ditolak.
  • Jika hook-mu hanya mencatat log, ubah. Hook yang exit 0 tidak memblokir apa pun.
  • Pindahkan satu tugas yang membaca banyak file ke .claude/agents/<name>.md dengan model: yang di-pin. Sebut namanya di prompt-mu sampai Claude memilihnya sendiri.
  • Periksa satu fakta yang belakangan ditebak Claude untukmu. Jika lebih baru dari model, tambahkan server docs dengan claude mcp add --transport http <name> --scope project <url> dan commit .mcp.json.
  • Periksa versi Claude Code-mu sebelum mengandalkan sebuah setting. onFailure: "block" butuh 2.1.295.
  • Jalankan git add -A && git status dan commit file barunya. Run kami menunjukkan tepat empat.

Lanjutkan

Kartu pemilih. Satu halaman. Ini baris-baris docs s1 dengan keempat tugas kami tertulis di atasnya. Bintang paket ini adalah keempat file di atas ditambah kartu ini.

Jenis Fungsinya Pakai saat Tugas kami File kami
CLAUDE.md Context persisten yang dimuat di setiap percakapan Konvensi yang dua kali salah dilakukan Claude Satu baris, bukan prosedur tidak ada
Skill Instruksi dan alur kerja yang bisa dipakai Claude Kamu menempel playbook yang sama untuk ketiga kalinya Checklist rilis .claude/skills/release/SKILL.md
Hook Script yang dipicu event Kamu ingin itu terjadi setiap kali, tanpa bertanya Jangan pernah edit data tersimpan secara manual .claude/settings.json
Subagent Context terisolasi yang mengembalikan hasil berupa ringkasan Tugas sampingan membanjiri percakapanmu Audit konvensi .claude/agents/conventions-auditor.md
MCP Terhubung ke layanan eksternal Kamu terus menyalin data dari tab yang tidak terlihat Claude Docs Claude Code .mcp.json
Plugin Membundel keempatnya jadi satu unit yang bisa diinstal Repository kedua butuh setup yang sama Tidak dipakai tidak ada

Kartu menentukan jenis, bukan kualitas. Hook yang hanya mencatat log, subagent yang tidak kamu sebut namanya, dan server untuk docs yang sudah diketahui model, semuanya berada di jenis yang tepat dan gagal. Pilih jenisnya, lalu buat file itu menjawab.

Kapan tidak perlu memakai ini.

  • Konvensi yang terus salah dilakukan Claude tetap di CLAUDE.md sebagai satu baris, bukan sebagai prosedur.
  • Tugas yang kamu arahkan bolak-balik, dengan riwayatmu terlihat, tetap di thread utama, bukan di subagent.
  • Server docs untuk library yang sudah diketahui model memakan context dan tidak mengubah apa pun.
  • pnpm test adalah CLI yang sudah kamu punya. Itu bukan tugas MCP.

Plugin. Plugin adalah kotak yang mengirim keempatnya bersama. Pakai saat repo lain butuh setup yang sama.

Skill plus MCP. Docs memasangkan keduanya: server memberi koneksi, dan skill mengajari Claude cara memakainya dengan baik.

Skill dengan context: fork. Sebuah skill bisa jalan di subagent yang di-fork, sehingga pembacaannya tetap di luar window-mu. Uji pada satu tugas sebelum kamu mengandalkannya.

Jenis hook selain command. Command yang kami pakai. Referensi hook mendaftar jenis handler lain, termasuk hook http dan prompt. Aturannya sama: apa yang dikembalikan hook yang menentukan, dan hook yang diam tidak memutuskan apa pun.

MCP versus CLI. Ada perdebatan terbuka soal server MCP yang memakan context, dengan satu tulisan yang membela CLI lebih dulu s11 dan satu lagi soal biaya context server MCP s12. Aturan kami: jika CLI sudah bisa mengerjakannya, pakai itu. Pakai MCP untuk yang tidak punya CLI. Tool search kini hanya memuat nama, jadi ukur dengan /context di setup-mu sendiri sebelum memutuskan s5.

Jaga skill tetap singkat dan terverifikasi. Satu skill singkat per tugas, dicek terhadap run nyata. Lihat makalah SkillsBench s13 dan pandangan praktisi tentang mengapa skill murah sampai dipakai s14.

FAQ

Kenapa hook-ku jalan tetapi edit tetap lolos? Hook hanya memutuskan lewat apa yang dikembalikan perintahnya. Exit 0 tanpa output bukan keputusan s2. Kembalikan JSON deny, atau exit 2.

Kenapa Claude membaca file sendiri padahal aku punya subagent? Pada run kami, file agent saja mendelegasikan 0 dari 2 run. Sebut namanya di prompt, misalnya "use the conventions auditor subagent".

Apakah hook mencakup semua yang bisa dilakukan Claude? Tidak. Milik kami cocok dengan Edit dan Write. Claude menawarkan membuat perubahan lewat shell pada run berikutnya, jadi cakup shell atau jauhkan dari daftar allow-mu.

Haruskah aku menambah server MCP untuk setiap library yang kupakai? Tidak. Jika model sudah mengenal library itu, server tidak mengubah apa pun dan memakan context. Tambahkan untuk data yang lebih baru dari model atau di luar disk-mu, dan cocokkan versi yang terinstal dengan versi yang menambahkan fiturnya.

Bisakah aku commit semua ini? Bisa. Skill, file settings, file agent, dan .mcp.json semuanya ada di repo, jadi siapa pun yang meng-clone-nya mendapatkannya. Setujui server MCP project sekali di sesi berikutnya.

File