Claude Code Mods Rehberi: İlk Modu Yazma, tool.call Hook'ları, mods API ve Güvenlik
Claude Code'u kendi iş akışınıza göre şekillendirmek için bugüne kadar elinizde dört ana araç vardı: settings.json'a yazılan hook'lar, SKILL.md dosyalarıyla skill'ler, özel status line ve MCP sunucuları. Hepsi güçlü, ama hepsinin ortak bir sınırı var: Claude Code'un dışında çalışıyorlar. Bir script çalıştırıyor ya da Claude'a metin veya araç veriyorlar. Terminalin yanına kendi panelinizi açamıyor, spinner'ın yanına bir sayaç ekleyemiyor, riskli bir komutu bekletip kullanıcıya düğmeli bir soru soramıyorsunuz. Claude Code v2.1.287 ile gelen mods (Claude Mods) tam olarak bu boşluğu dolduruyor: Claude Code'un kendi sürecinin içinde çalışan, olaylara bağlanan JavaScript veya TypeScript fonksiyonları.
Bu yazıda mod kavramını, kurulumunu, Claude'a mod yazdırmayı, sıfırdan ilk modu, araç çağrılarını ve istemleri yöneten olayları, mods API'yi, test sürecini, güvenlik modelini ve kurumsal kısıtlamaları anlatıyorum. Bütün komut, olay adı, ayar ve sürüm bilgileri Claude Code'un resmi dokümantasyonundan (code.claude.com/docs) ve resmi CHANGELOG'dan alındı; 5 Kasım 2026 itibarıyla kontrol edildi. Olay ve metotlar sürümden sürüme değişebildiği için şüphede kaldığınızda kendi sürümünüzün tip dosyalarına güvenin.
Claude Code Mod Nedir?
Resmi tanıma göre bir mod, Claude Code'un nasıl göründüğünü ve nasıl davrandığını değiştiren bir plugin'dir. Modun içi JavaScript veya TypeScript olay işleyicilerinden (event handler) oluşur: bir olay gerçekleştiğinde, örneğin bir araç çağrısı yapıldığında, bir istem gönderildiğinde ya da arayüzün bir parçası çizilirken Claude Code ilgili işleyiciyi çağırır. İşleyici olayı izleyebilir, değiştirebilir veya tamamen devralabilir.
Terim karmaşasına dikkat: bildiğimiz hook'lar ayar dosyasında tanımlanan kabuk komutu, HTTP isteği veya istemdir. Dokümantasyon modun işleyicisine "hook", ayar dosyasındaki türe "settings hook" diyor; bu yazıda da aynı ayrımı kullanıyorum.
Bir modun yapabildiği, diğerlerinin yapamadığı şeyler
Dokümantasyon modun öne çıkan yeteneklerini beş başlıkta topluyor:
- Kullanılabilir bir arayüz çizmek: transkriptin yanında bir pane (yan panel) ya da istem kutusunun hemen üstünde bir band; sekmeler, düğmeler ve metin alanlarıyla.
- Claude Code'un kendi arayüzünü yeniden çizmek: araç çağrısı satırı, spinner veya soru diyaloğu gibi parçaları değiştirmek.
- Bir araç çağrısına veya isteğe araya girmek: çağrıyı bekletmek, aracı çalıştırmadan yanıtlamak ya da bir isteği farklı bir modele göndermek.
- Bir komutta kendi kodunu çalıştırmak: Claude turu başlatmadan, hatta Claude çalışırken bile fonksiyonunuzu anında çalıştıran bir
/komut. - Hook'lar arasında veri paylaşmak: bir modun hook'ları dosyasındaki değişkenleri paylaşır; biri araç çağrılarını sayarken diğeri sayıyı spinner'ın yanında gösterebilir.
Modlar Claude Code CLI'da, Claude Desktop uygulamasının Code sekmesinde ve VS Code eklentisinde çalışıyor.
Mod, Settings Hook, Skill ve MCP Sunucusu: Hangisini Seçmeliyim?
Bu dört mekanizma birbiriyle örtüşüyor ve bir plugin hepsini birden taşıyabiliyor. Resmi karşılaştırma tablosunu Türkçeye aktardım:
| Mod | Settings hook | Skill | MCP sunucusu | |
|---|---|---|---|---|
| Nedir | Bir plugin içinde, Claude Code'un kendi sürecinde çağrılan fonksiyonlar | Bir yaşam döngüsü olayında çalışan kabuk komutu, HTTP isteği veya istem | Claude'un okuduğu talimatlar içeren bir SKILL.md dosyası | Claude'a araç veren harici bir süreç veya servis |
| Neyi değiştirebilir | Araç çağrıları, istemler, komutlar, turlar ve arayüzün çizdikleri | Bir araç çağrısının/istemin devam edip etmeyeceği, araç argümanları ve sonucu, Claude'a eklenen bağlam | Claude'un ne bildiği ve ne yaptığı | Claude'un hangi araçlara sahip olduğu |
| Arayüze çizebilir mi | Evet | Hayır | Hayır | Hayır |
| Ne yazarsınız | JavaScript veya TypeScript | Bir script ve bir settings.json girdisi | Markdown | Herhangi bir dilde sunucu |
| Ne zaman seçilir | Bir pane, istem üstü band, özel komut ya da bir olayı yeniden yazmak istediğinizde | Elinizdeki bir script ile bir olayı engellemek, izin vermek veya loglamak istediğinizde | Aynı talimatları sohbete tekrar tekrar yapıştırıyorsanız | Claude'un harici bir sisteme erişmesi gerektiğinde |
Pratik bir kural olarak: zaten çalışan bir bash script'iniz varsa ve tek derdiniz bir komutu engellemekse settings hook yeterli; tekrar eden talimatlar için skill, harici bir sisteme erişim için MCP doğru tercih. Ekrana çizmek ya da bir olayı yeniden yazmak istiyorsanız mod gerekiyor.
Hangi Sürüm Gerekli, Modlar Nerede Çalışır?
Modlar varsayılan olarak açık. Terminalde Claude Code v2.1.287 veya üstü gerekiyor; resmi CHANGELOG'un 2.1.287 girdisinde "Added Claude Mods: plugins may now modify deeper behavior" satırı yer alıyor. Desktop uygulaması kendi Claude Code kopyasını içerdiği için orada modlar v2.1.286'dan itibaren çalışıyor. Sürümünüzü kontrol etmek için:
# Terminal
claude --version
# Desktop uygulamasında, Code sekmesindeki yerel bir oturumda
/status
Bir modun hook'ları, plugin'i yükleyen her oturum türünde ve modeli hangi sağlayıcı sunuyorsa (Amazon Bedrock, Google Cloud Agent Platform, Microsoft Foundry dahil) çalışıyor. Çizim tarafı ise daha dar:
| Claude Code'u nerede çalıştırıyorsunuz | Hook'lar çalışır mı | Modun çizdikleri görünür mü |
|---|---|---|
Terminalde claude (editörün entegre terminali ve JetBrains plugin'i dahil) | Evet | Evet |
| Desktop uygulamasının Code sekmesi (WSL oturumu hariç) | Evet | Evet; yalnızca terminale özgü işaretlenen öğeler hariç |
| Desktop uygulamasında WSL oturumu | Hayır (WSL oturumlarında plugin yok) | Hayır |
| VS Code eklentisinin sohbet paneli | Evet | Evet, eklenti 2.1.299 veya üstüyle (feature flag çekmeyen oturumlarda, örneğin Bedrock'ta 2.1.304+) |
claude -p ve Agent SDK | Evet | Hayır |
| claude.ai veya mobil uygulamadan Remote Control | Evet, makinenizdeki oturumda | Makinenizdeki terminalde |
| Bulut oturumu (Claude Code on the web) | Evet, bulut oturumuna ulaşan bir plugin için | Hayır |
Print mode ve otomasyon tarafını CLI otomasyon rehberinde anlatmıştım.
Hazır Bir Mod Nasıl Kurulur?
Mod bir plugin olduğu için kurulumu da plugin kurulumuyla aynı: bir marketplace'ten, plugin adı + @ + marketplace adı ile. Örneğin your-org adlı bir marketplace'teki token-chart plugin'i için:
# Claude Code oturumunun içinde
/plugin install token-chart@your-org
# Kabukta
claude plugin install token-chart@your-org
# Oturum açıkken kabuktan kurduysanız, oturumda yükleyin
/reload-plugins
Marketplace, kapsam (scope) ve güncelleme kuralları plugin'lerle birebir aynı; bu konuyu plugin ve marketplace rehberinde ayrıntılı işlemiştim.
Anthropic'in örnek modları
Anthropic, anthropics/claude-code-playground deposunun claude-code/mods klasöründe örnek modlar paylaşıyor. (destek verilmeden, olduğu gibi):
token-weather: istem kutusunun üstüne bağlam penceresinin (context window) bir "hava durumu tahminini" çizer.blast-radius:rm -rfveya force push gibi riskli bir kabuk komutunu bekletir, neyi değiştireceğini gösterir ve devam/iptal düğmeleri sunar.replay-theater: son turda Claude'un yaptığı dosya düzenlemelerini adım adım gezen bir/replaykomutu ekler.
Birini denemek için depoyu klonlayıp modun klasörünü --plugin-dir ile tek bir oturum için yüklemeniz yeterli.
Claude Code'un içindeki yerleşik modlar
İlginç bir detay: Claude Code'un bazı kendi özellikleri de birer mod. /plugin'in Installed sekmesinde Built-in başlığı altında görünüyorlar: AGENTS.md'yi yükleyen cc-plugin-agents-md, /diff panelini çizen cc-plugin-diff, Claude'a mod yazma skill'ini veren cc-plugin-plugin-authoring, kurumun yönettiklerini kullanıcı modlarından koruyan cc-plugin-sec-default, cc-plugin-telemetry ve siz açana kadar kapalı olan "You should know" (cc-plugin-you-should-know). Bunların bir kısmının kaynak kodu anthropics/claude-code deposunun mods klasöründe testleriyle birlikte açık.
Claude'a Mod Nasıl Yazdırılır?
En hızlı yol, ne istediğinizi etkileşimli bir oturumda anlatmak. Claude bunu yerleşik plugin-authoring skill'iyle yapıyor (siz de /plugin-authoring ile yükleyebilirsiniz):
- Modu tarif edin. Örneğin:
make a mod that shows the current git branch above the prompt. Claude modu oturumun mods klasöründe kendine ait bir dizine yazar:~/.claude/dev-mods/<oturum-kimliği>/<mod-adı>/.defaultveacceptEditsizin modlarında bu klasör korumalı bir yol (protected path) olduğundan her dosya için onayınız istenir. - Modu onaylayın. Claude ilk dosyayı kaydettiğinde Claude Code, oturum için hot reload açılsın mı diye sorar. Enable for this session derseniz modlar tur bittiğinde yüklenir ve her değişiklikte yeniden yüklenir; Not now derseniz dosyalar yerinde kalır ve o oturum bir dahaki başladığında yüklenir.
- Yüklendiğini kontrol edin.
/pluginçalıştırıp Tab ile Installed sekmesine geçin; mod orada listelenir ve oradan kapatılabilir. - Deneyin ve iterasyon yapın. Değişikliğin yüklenmediğini söyleyen bir transkript satırı görürseniz onu bir sonraki mesajınıza yapıştırın; Claude o satırı kendisi görmez.
Önemli bir kısıt: Claude'un yazdığı mod yalnızca onu yazan oturumda yüklenir ve Claude Code bu klasörü cleanupPeriodDays süresinden eski olduğunda siler. Modu saklamak için dizini ~/mods/git-branch gibi kendi klasörünüze kopyalayıp claude --plugin-dir ~/mods/git-branch ile başlatın ya da bir marketplace'e ekleyin.
Claude'un yazdığı mod şu oturumlarda yüklenmez: onay verecek kimsenin olmadığı oturumlar (claude -p veya dontAsk modu), güven isteminin kabul edilmediği çalışma alanları ve modların kapalı olduğu oturumlar (--safe-mode, --bare, disableAllHooks ya da kurumun yönetilen ayarlarıyla engellenmiş olması).
İlk Modunuzu Sıfırdan Yazmak: Adım Adım
Modun nasıl çalıştığını anlamanın en iyi yolu resmi eğitimdeki first-mod örneğini kendiniz yazmak. Bu mod Claude'un araç çağrılarını sayıyor, sayıyı Claude çalışırken spinner'ın yanında gösteriyor ve sayıyı yazdıran bir /tally komutu ekliyor. Node.js'e, bundler'a ya da build adımına ihtiyacınız yok; Claude Code .js ve .ts dosyalarını doğrudan yüklüyor. Toplam üç dosya var:
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
1. Dizinleri ve manifest'i oluşturun
mkdir -p first-mod/.claude-plugin first-mod/hooks
Mod bir plugin olduğu için bir manifest'e ihtiyaç duyar; bu örnekte özel bir alan yok. first-mod/.claude-plugin/plugin.json:
{
"name": "first-mod",
"version": "0.1.0",
"description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
"author": { "name": "Your Name" }
}
2. Claude Code'a kodun yerini söyleyin
Claude Code bir plugin'i yüklerken hooks/hooks.json dosyasını okur. Bu dosyadaki modules anahtarı kodunuzun yolunu verir ve bir plugin'i mod yapan şey bu anahtarın varlığıdır. Yol, hooks.json'a göre görelidir. first-mod/hooks/hooks.json:
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}
3. Hooks modülünü yazın
Mod yüklendiğinde Claude Code dosyanın dışa aktardığı register fonksiyonunu çağırır ve ona on adlı bir fonksiyon verir. Her on çağrısı, adını verdiği olay için bir hook kaydeder. first-mod/hooks/register.js:
// Aşağıdaki hook'ların paylaştığı sayaç
let calls = 0
export function register(on) {
on('session.start', async ($, e, next) => {
await $.command.register({
name: 'tally',
description: 'Show how many tool calls Claude has made',
})
return next(e)
})
on('tool.call', async ($, e, next) => {
calls += 1
$.ui.invalidate('ui.render')
return next(e)
})
on('command.run', { command: 'tally' }, async () => {
return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
})
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}
4. Yükleyin ve deneyin
--plugin-dir bayrağı bir plugin dizinini kurmadan, tek bir oturum için yükler:
claude --plugin-dir ./first-mod
Claude'dan birkaç araç çağrısı gerektiren bir şey isteyin (örneğin list the files here and read the README). Claude çalışırken spinner Thinking · tool calls: 2… gibi artan bir sayı gösterir. İş bitince /tally yazın; transkriptte first-mod: Claude has made 2 tool calls since this mod loaded görürsünüz. Claude Code, komut metninin önüne plugin'in adını koyar. Komutu claude -p "/tally" --plugin-dir ./first-mod ile etkileşimsiz modda da deneyebilirsiniz.
/tally komut listesinde yoksa modül yüklenmemiş demektir. Claude Code --plugin-dir ile yüklenen dizini izliyor; oturum açıkken bir dosyayı değiştirip kaydettiğinizde hooks modülü sıcak yeniden yükleniyor ve transkriptte bunu bildiren bir satır çıkıyor.
Her yeniden yükleme register'ı tekrar çalıştırdığı için calls sıfırlanır; kalıcı değerler için $.state veya oturumlar arası JSON deposu $.store kullanılıyor.
Her hook'un üç argümanı: $, e ve next
Claude Code her hook'a aynı üç argümanı geçiyor:
$(mods API): modun kendi dışına uzanmak için çağırabileceği tüm metotlar;$.ui,$.commandgibi isim alanlarında.e(olay): olayın girdisi, düz veri olarak; örneğin bir araç çağrısının adı ve argümanları. Olay derinlemesine dondurulmuştur (deeply frozen); bir alana atama yapmak hata fırlatır.next: olayı diğer modlara ve en sonunda Claude Code'un kendi davranışına aktaran, sonucu döndüren fonksiyon.
Aynı olaya bağlı işleyiciler bir middleware zinciri oluşturuyor: next(e) döndüren hook olayı izler, değiştirilmiş kopyayla next çağıran yeniden yazar, next'i çağırmadan sonuç döndüren yanıtlar ve zinciri kısa devre yapar. first-mod'da command.run yanıtlıyor, ui.render yeniden yazıyor, diğerleri izliyor.
Matcher ile olayları filtrelemek
on'a ikinci argüman olarak verilen nesneye matcher deniyor. Alanları olayın alanlarıyla karşılaştırılıyor ve hook yalnızca her alan eşleşirse çalışıyor. Bir alan tek değer ({ tool: 'Bash' }), dizi ({ tool: ['Edit', 'Write'] }) ya da düzenli ifade ({ tool: /^mcp__github__/ }) olabiliyor.
Olay adı joker de olabiliyor: 'classic.*' tüm settings hook olaylarıyla, '*' ise telemetri olayları hariç tüm olaylarla eşleşiyor. Bir olayı her matcher için yalnızca bir kez kaydetmelisiniz; session.start'ı matcher'sız iki kez kaydederseniz modül on("session.start") is registered twice without a matcher hatasıyla yüklenmez. Oturum başında yapılacak her şeyi tek bir hook'ta toplayın.
Araç Çağrılarını Korumak: tool.call ve tool.check
Modların en pratik kullanım alanlarından biri, Claude'un yapacağı araç çağrılarını kontrol etmek. tool.call, Claude Code bir aracı çalıştırmak üzereyken tetikleniyor; subagent'ların çağrıları ve MCP araç çağrıları da buna dahil. e.tool aracın adı, aracın argümanları ise e'nin alanları (Bash için e.command gibi). next(e) çağırdığınızda Claude Code önce izin kontrolünü, sonra aracı çalıştırıyor.
Force push'u reddeden ve Claude'a nedenini söyleyen resmi örnek:
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
if (/git push .*--force/.test(e.command)) {
return { deny: 'Force pushes are not allowed in this repository. Push to a new branch instead.' }
}
return next(e)
})
Komut çalışmaz, izin istemi de çıkmaz; Claude deny metnini aracın sonucu olarak okur, bu yüzden metni Claude'un uygulayabileceği bir talimat gibi yazın. Bir tool.call hook'u ayrıca argümanları değiştirebilir, { result } döndürerek aracı çalıştırmadan çağrıyı yanıtlayabilir ya da await next(e) sonrası { deny } ile sonucu Claude'dan saklayabilir (aracın yaptığını geri almaz).
Kurumun yönetilen ayarlarındaki hook'lar her modun tool.call hook'undan önce çalışıyor.
Riskli bir komutu kullanıcı karar verene kadar bekletmek
Bir tool.call hook'u next'i çağırmadan önce await edebiliyor ve araç çağrısı o süre boyunca bekliyor. Kullanıcıya soru sormak için $.ui.ask kullanılıyor: sorunuzu, Claude'un size soru sorduğu diyalogda numaralı seçeneklerle gösteriyor ve kullanıcının seçtiği etiketle çözülüyor. Resmi örnek:
const RISKY = /\brm\s+-rf?\b|\bgit\s+reset\s+--hard\b|\bgit\s+push\b.*--force/
export function register(on) {
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
if (!RISKY.test(e.command)) return next(e)
let answer = 'Refuse'
try {
answer = await $.ui.ask('Run this command? ' + e.command, ['Run it', 'Refuse'])
} catch {
// kullanıcı soruyu kapattı ya da bu bir claude -p çalışması
}
if (answer !== 'Run it') {
return { deny: 'The user declined this command. Ask before trying a different approach.' }
}
return next(e)
})
}
Dokümantasyonun da belirttiği gibi bu desen git push -f gibi başka yazımları kaçırıyor. Kullanıcı Run it derse next(e) çağrılıyor ve normal izin kontrolü yine çalışıyor; soruyu kapatırsa ya da claude -p çalışmasındaysanız $.ui.ask reddediliyor ve cevap Refuse'da kalıyor. Beklemeyi $.ui.ask gibi bir mods API çağrısının içinde tutun; o süre hook'un zaman sınırına sayılmıyor.
Hook hata verirse ne olur? .catch ile "fail closed"
Güvenlik modlarında en çok gözden kaçan nokta: .catch işleyicisi olmayan bir hook next'i çağırmadan önce hata fırlatır, süre aşar ya da yanlış biçimde sonuç döndürürse Claude Code onu atlar; yani guard'ınız çökerse komut mod yokmuş gibi çalışır. Bunu tersine çevirmek için on'un döndürdüğü kayda .catch bağlıyorsunuz:
on('tool.call', { tool: 'Bash' }, guard).catch(async ($, e, next) => {
if (next.called) return next(e)
return { deny: 'The command guard failed, so this command was not run: ' + next.error.kind }
})
.catch işleyicisinin süre sınırı 1 saniye. claude plugin validate çıktısındaki gating hook without .catch: tool.call satırı, .catch'i olmayan engelleyici hook'ları gösteriyor.
tool.check: İzin kararına doğrudan katılmak
İzin kararına katılmak için tool.check olayı var. Sabit bir komut için Bash(npm test) gibi bir izin kuralı yeterli; tool.check kararın o anki duruma (örneğin git dalına) bağlı olduğu durumlar için. PreToolUse hook'larından sonra, izin istemi ve auto mode sınıflandırıcısından önce tetikleniyor; await next(e) Claude Code'un kendi kararını (allow, ask, deny) veriyor, argümanlar e.input altında:
on('tool.check', { tool: 'Bash' }, async ($, e, next) => {
const decided = await next(e)
if (!e.input.command.includes('git push')) return decided
const branch = await $.process.run(['git', 'branch', '--show-current'])
if (branch.stdout.trim() !== 'main') return decided
return { decision: 'deny', reason: 'Push from a branch other than main' }
})
Bu hook main dalındayken git push'u bir kural izin verse bile reddediyor. Komut metnini eşleştirdiği için bunu Claude'a bir hatırlatma olarak görün; herkes için engellemek istiyorsanız dalı Git sunucunuzda koruyun. Plan modunda, salt okunur olmayan çağrılarda hook kararı sıkılaştırabilir ama gevşetemez.
İstemleri Zenginleştirmek: prompt.submit
prompt.submit hook'u her istemi tur başlamadan önce görüyor: next({ ...e, text: newText }) istemi yeniden yazıyor, e.context'e eklenen metni yalnızca Claude okuyor, { drop: 'the reason' } ise istemin gönderilmesini durduruyor.
Resmi örnek, istem bir pull request'ten bahsettiğinde Claude'a mevcut dal adını ekliyor:
on('prompt.submit', async ($, e, next) => {
if (!/\bPR\b|pull request/i.test(e.text)) return next(e)
const git = await $.process.run(['git', 'branch', '--show-current'])
if (git.exitCode !== 0) return next(e)
return next({ ...e, context: [...(e.context ?? []), 'Current branch: ' + git.stdout.trim()] })
})
open a PR for this change yazdığınızda mesajınız aynı görünüyor ama Claude arkasından Current branch: feature/auth gibi bir satır da okuyor. drop döndürürseniz metin istem kutusuna geri dönüyor ve kullanıcı Prompt dropped by a hook: ile nedeninizi görüyor. İstek başına değişen metin ekleyen hook'lar prompt cache'i geçersiz kılıyor (bkz. maliyet ve context yönetimi rehberi).
Mods API ile Komut, Araç, Model Çağrısı ve Zamanlayıcı
Bir hook kendi kodunun dışında bir şey yapmak (çizmek, model çağırmak, dosya okumak, süreç başlatmak) için mods API'yi çağırmak zorunda; bu yüzden Claude Code bir modun ne yaptığını kurulumdan önce listeleyebiliyor. Modülde Node.js API'leri ve setTimeout yok; URL, TextEncoder, crypto.subtle gibi standart web API'leri var.
Kullanıcı için komut, Claude için araç
Komut kullanıcı içindir: session.start'ta $.command.register ile kaydedip command.run'da yanıtlıyorsunuz. Dönen text transkripte basılıyor ve Claude da okuyor; Claude çalışırken de çalışması için immediate: true ekleyin. Yerleşik bir komut adı seçerseniz $.command.register hata fırlatıyor ve session.start'ın geri kalanı çalışmıyor; komutları o hook'ta en son kaydedin.
Araç ise Claude içindir: $.tool.register ile bir ad, Claude'un okuyacağı açıklama ve girdi için JSON Schema veriyorsunuz. Claude aracı mcp__ + plugin adı + __ + kayıtlı ad biçiminde görüyor (örneğin my-mod plugin'inde ticket → mcp__my-mod__ticket) ve çağrılarını bu tam ada filtrelenmiş bir tool.call hook'unda { result } döndürerek yanıtlıyorsunuz.
Modun kendi model çağrıları: $.model.complete ve $.model.fork
Bir mod küçük işler için kendi model isteklerini gönderebiliyor. $.model.complete isteminizi verdiğiniz modele tek başına gönderiyor (CLAUDE.md ve araç yok). $.model.fork({ prompt }) ise oturumun modeliyle mevcut konuşmayı ve sonuna isteminizi gönderiyor; konuşma cache'teyken isteğin çoğu prompt cache'ten sunuluyor. CHANGELOG 2.1.318'e göre $.model.fork artık effort seçeneği alıyor; hızlı bir yan iş oturumdan bağımsız olarak low ile çalışabiliyor.
Her iki çağrı da oturumun kimlik bilgilerini kullanıyor; faturası kullanıcının planına, API anahtarına veya bulut sağlayıcısına yazılıyor. Dokümantasyondaki /triage örneği, $.model.complete({ model: 'haiku', system: 'Reply with one word: bug, feature, or question.', prompt: e.args, maxTokens: 20, timeoutMs: 15000 }) ile komuttan sonra yazılan metni etiketliyor. Claude API hatası çağrıyı reddetmiyor; bu yüzden r.isAnswered'ı kontrol edip false ise r.reason'ı okuyun. maxTokens varsayılanı 1024.
Arka plan işleri
Bir olaydan uzun süren işler için session.start'ta başlatılan zamanlayıcılar kullanılıyor: $.clock.every ve $.clock.after, setInterval ve setTimeout'un yerini alıyor. Dokümantasyondaki örnek, bir PR'ın kontrollerini dakikada bir gh pr checks ile sorgulayıp sonucu $.ui.status ile istem kutusunun altında gösteriyor. Callback olayların dışında çalışıyor, tur başlatmıyor; hata fırlatırsa hata debug loga gidiyor ve zamanlayıcı bir sonraki aralıkta yeniden çalışıyor.
Dosya, süreç, ağ ve oturumlar arası mesaj
Mod, dosya sistemine, süreçlere ve ağa Claude Code'u çalıştıran kullanıcının yetkileriyle erişiyor: $.fs (read, write, exists, stat, list; dosya başına 4 MiB), $.process (run(['git', 'status']) kabuk kullanmadan komut çalıştırır, varsayılan zaman aşımı 30 saniye, en fazla 10 dakika; program başlayamazsa reddedildiği için try/catch kullanın), $.http (fetch; en fazla 5 yönlendirme, farklı origin'e yönlendirmede Authorization gibi başlıklar düşer), $.store (plugin'e özel, oturumlar arası JSON deposu, toplam 4 MiB), $.env, $.settings, $.mcp ve $.session. $.session.send, SendMessage aracıyla aynı teslimatı yaparak başka bir oturumunuza veya subagent'a mesaj gönderiyor (bkz. cross-session messaging rehberi). Her mods API çağrısı da bir olay ($.fs.read için fs.read); zincirde önce çalışan bir mod sizin çağrınızı reddedebiliyor. Kurumlar modların eriştiği yerleri bu yolla kısıtlıyor.
Bir Modu Doğrulamak ve Test Etmek
claude plugin validate: Modu Claude Code'un gözüyle görmek
Kodu çalıştırmadan, oturum başlatmadan bir modun ne yaptığını görmek için claude plugin validate kullanılıyor. Komut manifest'i kontrol ediyor ve Claude Code'un mod yüklerken yaptığı statik analizi hooks modülünün kaynağı üzerinde çalıştırıyor:
claude plugin validate ./first-mod
# ❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
# ❯ ./register.js calls: $.command.register, $.ui.invalidate
#
# ✔ Validation passed
hooks: satırı bağlanılan olayları (filtreleri süslü parantezde), calls: satırı çağrılan her mods API metodunu listeliyor. Yanlış yazılmış bir olay adı "tool.calls" is not an event hatası veriyor.
Statik analizin her şeyi bulabilmesi için kurallar var: mods API çağrılarını $.store.get('notes') gibi tam yazın (const ui = $.ui $.ui is used as a value hatası verir), olay adını on'a string literal olarak verin, register içinde on adını gölgelemeyin, yalnızca plugin dizinindeki dosyaları göreli yolla ve dosya başında import ile alın (izin verilen tek çıplak import claude-code); dinamik import() ve require yok.
Sürümünüze özel tip dosyaları
Claude Code, --plugin-dir ile etkileşimli oturumda yüklenen ya da Claude'un yazdığı bir modun .claude-plugin/types/ klasörüne TypeScript bildirim dosyaları yazıyor. claude-code/index.d.ts çalıştırdığınız sürümdeki her olayı, mods API metodunu ve çizilebilir öğeyi tanımlıyor; böylece editörünüz otomatik tamamlama ve tip kontrolü yapabiliyor. Sayfalarla çeliştiğinde bu dosyalara güvenin.
claude plugin test ile otomatik testler
Modlar için oturum, oturum açma veya ağ olmadan kabuktan claude plugin test ile çalışan otomatik testler yazılabiliyor. Test dosyası claude-code/testing'den test ve expect alıyor; $.tool.call({ tool: 'Bash', command: 'ls' }) gibi çağrılarla olay tetikleyip $.command.run({ command: 'tally', args: '' }) sonucunu expect(...).toBe(...) ile kontrol ediyor. Testin içinde on('tool.call', () => ({ result: 'ok' })) ile araç çağrılarını Claude Code yerine yanıtlayarak hiçbir aracın gerçekten çalışmamasını sağlıyorsunuz. Bir testin varsayılan süre sınırı 5 saniye.
Gerçek Bir İş Akışı: Ekip İçin Güvenlik Modu
Parçaları birleştirelim: riskli komutların sorulmadan çalışmasını ve main'den push'u engellemek, PR'lardan bahsedildiğinde Claude'un dalı bilmesini istiyorsunuz:
- Iskeleti oluşturun:
team-guard/.claude-plugin/plugin.jsonve"modules": ["./register.js"]içerenteam-guard/hooks/hooks.json.claude-ile başlayan adlarıvalidatereddediyor. - Hook'ları tek modülde toplayın:
$.ui.ask'litool.callguard'ı (riskli komutlar),maindalını kontrol edentool.checkhook'u ve dal adını bağlama ekleyenprompt.submithook'u. Guard'a mutlaka.catchekleyin ki çökerse komutu geçirmek yerine reddetsin. - Doğrulayın:
claude plugin validate ./team-guardçıktısındahooks:satırında üç olayı,calls:satırında$.ui.askve$.process.run'ı görmelisiniz;gating hook without .catchsatırı kalmamalı. - Geliştirme döngüsü:
claude --plugin-dir ./team-guardile açıp Claude'a değişiklik yaptırın; tur sonunda mod yeniden yüklenir. Kurulu kopya üzerinde geliştirmeyin; Claude Code kurulu plugin'leri sürüme göre önbelleğe alıyor. - Paylaşın: ekip için kendi marketplace'inize ekleyin ve README'de test ettiğiniz Claude Code sürümünü belirtin.
Bu tür bir modun bir güvenlik sınırı olmadığını unutmayın: komut metni eşleştirmesi farklı yazımları kaçırabilir. Gerçek kısıtlama için modu izin kuralları, sandbox ve Git sunucusundaki dal korumasıyla birlikte kullanın.
Bir Moda Güvenmeli miyim? Güvenlik Modeli
Dokümantasyonun en çok vurguladığı nokta: mod, sizin yetkilerinizle çalışan koddur ve sandbox'lanmaz. Dosya okuyup yazabilir, program başlatabilir, ağ isteği yapabilir; ortam değişkenlerinizi ve ayar dosyalarınızı (API anahtarları dahil) okuyabilir; her istemi ve araç çağrısını görüp yeniden yazabilir, sizin adınıza istem gönderebilir, size sorulmadan araç çağrısı onaylayabilir ve planınızla model çağırabilir. Sandbox açıkken bile bir modun başlattığı süreç sandbox dışında çalışır.
Yönetilen ayarlar varsa ya da oturum Team/Enterprise /login ile çalışıyorsa, bir modun onayı varsayılan olarak ask kurallarınızı ve PreToolUse hook'larınızı aşamıyor; ikisi de yoksa aşabiliyor. Bir mod izin istemini değiştiremez.
Kurmadan önce plugin dosyalarını indirip claude plugin validate ./some-mod çalıştırın ve calls: satırında özellikle $.fs.write, $.process.run, $.http.fetch, $.env.get/$.settings.read (API anahtarı okuyabilir), $.model.complete (planınızı harcar), $.prompt.submit (sizin adınıza istem gönderir) ve $.session.send çağrılarına bakın.
Modları kapatmak
- Tek bir mod:
/plugin'in Installed sekmesinden plugin'ini devre dışı bırakın veya kaldırın. - Tüm kurulu modlar, tek oturum: Claude Code'u
--safe-modeile başlatın (diğer özelleştirmelerinizi de kapatır). - Kurduğunuz tüm modlar, her oturumda:
~/.claude/settings.json'a"disableAllHooks": trueyazın; settings hook'larınız ve özel status line'ınız da durur, kurumunuzun yönettikleri çalışmaya devam eder.
Bu ayarlar yerleşik modları durdurmuyor. Yüklü modları /plugin'deki 1 mod active · first-mod gibi soluk satırda görürsünüz.
Kurumlar Modları Nasıl Yönetir?
Varsayılan olarak modlar açık; makinede yönetilen ayarlar (managed settings) varsa ya da oturum Team/Enterprise /login ile çalışıyorsa, kullanıcının kurduğu her modun önünde yerleşik sec-default@builtin guard'ı yükleniyor. Guard, kullanıcı modlarının yönetilen hook'larınızı, sistem istemini, yönetilen CLAUDE.md'yi ve yönetilen MCP araçlarınızı değiştirmesini engelliyor.
Kullanıcıların getirdiği hiçbir modun hook'larının çalışmamasını istiyorsanız guard'ın allowManagedModsOnly seçeneğini yönetilen ayarlarda açın:
{
"pluginConfigs": {
"cc-plugin-sec-default@builtin": {
"options": {
"allowManagedModsOnly": true
}
}
}
}
Seçenek yalnızca yönetilen ayarlardan okunduğu için kullanıcı geri alamıyor; settings hook'ları, status line ve /goal etkilenmiyor. disableSideloadFlags ise --plugin-dir, --plugin-url, --agents ve --mcp-config'i reddediyor. Not: izin kuralları modun kendi $.fs/$.process çağrılarına uygulanmıyor; Read(.env) reddedilmiş olsa bile bir mod bu dosyayı $.fs.read ile okuyabiliyor.
Sık Karşılaşılan Sorunlar
- Mod hiçbir şey yapmıyor: önce
claude --versionile v2.1.287+ olduğunuzu, sonra/plugin'dekimods activesatırında modun adının geçtiğini kontrol edin. İlk kez açtığınız bir dizinde hiçbir mod yüklenmiyorsa, o dizindeclaudeile etkileşimli bir oturum açıp güven istemini kabul edin. validategeçiyor amahooks:satırı yok:hooks/hooks.json'damodulesanahtarı yok ya da yanlış yazılmış;"modules": ["./register.js"]ekleyin.hook skippedsatırı: hook hata fırlattı ya da süre aştı. Bir hook'un tek olay için kendi çalışma süresi 10 saniye (prompt.editiçin 50 ms);nextiçinde ve$.clock.sleepdışındaki mods API çağrılarında geçen süre sayılmıyor.
Ayrıntılı tanı için claude --debug ile başlatıp debug logu okuyun.
Sıkça Sorulan Sorular
Claude Code mod ile hook arasındaki fark nedir?
Settings hook'ları settings.json'da tanımlanan ve bir olayda çalışan kabuk komutu, HTTP isteği veya istemdir; Claude Code'un dışında çalışır. Mod ise bir plugin içinde, Claude Code'un kendi sürecinde çağrılan JavaScript/TypeScript fonksiyonlarıdır. Mod arayüze çizebilir, komut ve araç ekleyebilir, olayları yeniden yazabilir veya yanıtlayabilir; settings hook çizim yapamaz.
Mod yazmak için Node.js veya build adımı gerekir mi?
Hayır. Claude Code .js ve .ts dosyalarını doğrudan yükler; Node.js, bundler veya build adımına ihtiyaç yoktur. Hooks modülünde Node.js API'leri de bulunmaz; dosya, süreç ve ağ erişimi mods API ($.fs, $.process, $.http) üzerinden yapılır.
Modlar claude -p ve bulut oturumlarında çalışır mı?
Hook'lar claude -p, Agent SDK ve bulut oturumlarında (plugin bulut oturumuna ulaşıyorsa) çalışır, ama modun çizdikleri görünmez. Claude'un oturumda yazdığı modlar ise onay verecek kimse olmadığı için claude -p ve dontAsk modunda yüklenmez.
Bir mod güvenli mi, sandbox'ta mı çalışır?
Modlar sandbox'lanmaz ve sizin yetkilerinizle çalışır: dosya okuyup yazabilir, program başlatabilir, ağ isteği yapabilir, ortam değişkenlerinizi okuyabilir ve araç çağrılarını onaylayabilir. Yalnızca güvendiğiniz yazar ve marketplace'lerden kurun; kurmadan önce claude plugin validate ile hooks: ve calls: satırlarını inceleyin.
Claude'un yazdığı mod başka oturumlarda kullanılabilir mi?
Varsayılan olarak hayır; yalnızca onu yazan oturumda yüklenir ve ~/.claude/dev-mods/ altındaki klasör cleanupPeriodDays süresi dolunca silinir. Saklamak için dizini kendi klasörünüze kopyalayıp claude --plugin-dir ile yükleyin ya da bir marketplace'e ekleyin.
Şirketimde kullanıcıların mod kurmasını nasıl engellerim?
Yönetilen ayarlarda pluginConfigs altında cc-plugin-sec-default@builtin için allowManagedModsOnly: true ayarlayın. Kullanıcıların getirdiği hiçbir modun hook'ları çalışmaz, kurumunuzun modları ve yerleşik modlar çalışmaya devam eder. --plugin-dir'i de engellemek için disableSideloadFlags: true ekleyin.
Özet
Claude Code mods, v2.1.287 ile gelen ve Claude Code'u içeriden genişletmenin yolunu açan bir özellik. Bir mod, hooks/hooks.json'daki modules anahtarıyla bir hooks modülünü işaret eden sıradan bir plugin; modül register(on) fonksiyonuyla tool.call, tool.check, prompt.submit, command.run, ui.render gibi olaylara bağlanıyor ve her hook $, e, next üçlüsüyle olayı izliyor, yeniden yazıyor ya da yanıtlıyor. Mods API komut, araç, model çağrısı ve zamanlayıcı ekliyor; claude plugin validate ve claude plugin test modu çalıştırmadan inceliyor. Ama unutmayın: mod sizin yetkilerinizle, sandbox dışında çalışan kod; güvenlik guard'larınıza .catch ekleyin, yabancı modları kurmadan önce doğrulayın, kurumsal ortamda allowManagedModsOnly ve disableSideloadFlags ile sınır çizin.
Devam okumaları için: modların "dış" kardeşi olan settings hook'larını hooks rehberinde, modların dağıtıldığı plugin ve marketplace sistemini plugin marketplace rehberinde, modların izin kararlarıyla nasıl etkileştiğini anlamak için permissions rehberinde bulabilirsiniz.
Kaynaklar: Claude Code: Mods overview,
Create a mod,
React to events with a mod,
Use the mods API,
Draw in the interface with a mod,
Mods reference (limits),
Troubleshoot a mod,
Manage mods for your organization,
Claude Code CHANGELOG (2.1.287, 2.1.318).
Bu yazı Ahmet Bilgiç tarafından ahmetbilgic.com için yazılmıştır. Diğer yazılar için:
medium.com/@ahmet_bilgic07