Claude Code Mods: Erweiterungen, die in Claude Code laufen

Bisher ließ sich Claude Code nur von außen erweitern. Hooks starten ein Shell-Skript, Skills geben Claude Anweisungen, MCP-Server liefern zusätzliche Werkzeuge. Mit Version 2.1.287 vom 1. Oktober 2026 kommen Mods hinzu. Ein Mod ist JavaScript oder TypeScript, das innerhalb von Claude Code läuft, jeden Tool-Aufruf sehen und verändern kann und eigene Elemente in die Oberfläche zeichnet. Ein Teil von Claude Code selbst ist inzwischen als Mod gebaut.
Ich habe einen kleinen Guard-Mod geschrieben und in echten Sitzungen getestet, jeweils mit einer Gegenprobe ohne Mod.
Ein Plugin mit eigenem Code
Technisch ist ein Mod ein Plugin, dessen hooks/hooks.json auf eine Code-Datei zeigt. Drei Dateien reichen.
guard-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
Die hooks.json enthält nur den Verweis auf den Code.
{
"description": "guard-mod hooks module",
"modules": ["./register.js"]
}
Die Code-Datei exportiert eine Funktion register. Claude Code ruft sie beim Laden auf und übergibt eine Funktion on, mit der der Mod sich für Ereignisse anmeldet: einen Tool-Aufruf, einen abgeschickten Prompt, den Start einer Sitzung oder das Zeichnen eines Oberflächenteils. Einen Build-Schritt gibt es nicht, Claude Code lädt .js und .ts direkt.
Jeder Handler bekommt drei Argumente. $ ist die Schnittstelle zu Claude Code, über die der Mod zeichnet, Befehle registriert, Dateien liest oder ein Modell aufruft. e ist das Ereignis, etwa der Shell-Befehl, den Claude ausführen will. next reicht das Ereignis weiter an den nächsten Mod und am Ende an Claude Code selbst.
Der Mod beobachtet und ruft next(e) unverändert auf, er schreibt das Ereignis um und ruft next mit einer geänderten Kopie auf, oder er antwortet selbst und ruft next gar nicht auf. Im letzten Fall läuft das Werkzeug nie.
Der Unterschied zu Hooks
Die offizielle Doku grenzt die vier Erweiterungsarten so ab.
| Mod | Settings-Hook | Skill | MCP-Server | |
|---|---|---|---|---|
| Was es ist | Funktionen im Prozess von Claude Code | Shell-Befehl, HTTP-Aufruf oder Prompt pro Ereignis | Markdown mit Anweisungen | externer Prozess mit Werkzeugen |
| Was es ändern kann | Tool-Aufrufe, Prompts, Befehle, Runden und die Oberfläche | ob ein Tool-Aufruf oder Prompt weiterläuft, Argumente, Ergebnis, Kontext | was Claude weiß und tut | welche Werkzeuge Claude hat |
| Kann zeichnen | ja | nein | nein | nein |
| Geschrieben in | JavaScript, TypeScript | beliebiges Skript | Markdown | beliebige Sprache |
Der entscheidende Unterschied ist die Lebensdauer. Ein Settings-Hook startet für jedes Ereignis ein Skript, das danach beendet ist. Ein Mod wird einmal geladen und läuft für die ganze Sitzung.
Er kann also zählen, was passiert ist, Werte zwischen verschiedenen Ereignissen teilen und den Stand in einem Panel neben dem Transkript oder in einer Leiste über dem Eingabefeld anzeigen. Er kann einen Tool-Aufruf anhalten, den Nutzer fragen und erst dann entscheiden. Außerdem kann er Slash-Commands anlegen, die sofort eigenen Code ausführen, ohne dass ein Modell antwortet.
Anthropic nutzt das selbst. Das /diff-Panel und das Laden von AGENTS.md sind eingebaute Mods, deren Quellcode im Claude-Code-Repository liegt. Ebenfalls als Mod kommt “You should know”, ein Nebenagent, der bei längeren Aufgaben mitliest und Hinweise über dem Prompt einblendet. Er ist standardmäßig aus.
Ein Guard-Mod im Test
Für den Test habe ich einen Mod gebaut, der zwei riskante Befehle abfängt und Tool-Aufrufe zählt. Force-Pushes lehnt er grundsätzlich ab. Bei rekursivem Löschen fragt er den Nutzer und lehnt ab, wenn niemand antwortet.
let calls = 0
async function guard($, e, next) {
if (/\bgit\s+push\b.*(--force|\s-f\b)/.test(e.command)) {
return { deny: 'Force pushes are not allowed here. Push to a new branch instead.' }
}
if (/\brm\s+-[a-zA-Z]*r/.test(e.command)) {
let answer = 'Refuse'
try {
answer = await $.ui.ask('Run this command? ' + e.command, ['Run it', 'Refuse'])
} catch {
// claude -p oder abgebrochen: niemand kann antworten, also bleibt es bei Refuse
}
if (answer !== 'Run it') {
return { deny: 'The user did not approve this recursive delete. Ask before trying again.' }
}
}
return next(e)
}
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
return next(e)
})
on('tool.call', { tool: 'Bash' }, guard).catch(async ($, e, next) => {
return { deny: 'The command guard failed, so this command was not run: ' + next.error.kind }
})
on('command.run', { command: 'tally' }, async () => {
return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
})
}
Der .catch-Handler sorgt dafür, dass der Guard bei einem eigenen Fehler den Befehl ablehnt, statt ihn durchzulassen. Ohne ihn würde Claude Code einen abgestürzten Handler überspringen und den Befehl ausführen.
Vor dem ersten Start zeigt claude plugin validate, was der Mod tut, ohne ihn auszuführen.
❯ ./register.js hooks: session.start, tool.call, tool.call{tool=Bash}, command.run{command=tally}
❯ ./register.js calls: $.command.register, $.ui.ask
✔ Validation passed
Die erste Zeile listet die Ereignisse, die zweite alle Aufrufe an Claude Code. Netzwerkzugriffe, Prozessstarts oder Modellaufrufe tauchen hier auf, weil ein Mod sie nur über $ erreicht. Dazu kommt claude plugin test, das Unit-Tests ohne Sitzung und ohne Netz ausführt. Meine drei Tests liefen in 0,22 Sekunden durch.
Für den eigentlichen Test lief Claude Code 2.1.288 im nicht interaktiven Modus claude -p mit Haiku 4.5 in einem Wegwerf-Repository mit lokalem Remote. Bash war ausdrücklich erlaubt, und der Prompt bestätigte die Aktion, damit das Modell nicht schon von sich aus nachfragt.
| Befehl | Ohne Mod | Mit Mod |
|---|---|---|
rm -rf build | ausgeführt, Verzeichnis gelöscht | abgelehnt, Verzeichnis bleibt |
git push --force origin main | ausgeführt, Remote überschrieben | abgelehnt, Remote unverändert |
/tally | entfällt | “Claude has made 0 tool calls since this mod loaded” |
Claude bekommt die deny-Nachricht als Ergebnis des Werkzeugs und gibt sie weiter. Beim rekursiven Löschen greift die Rückfallregel, denn in claude -p kann niemand die Frage beantworten, also lehnt der Mod ab. In einer interaktiven Sitzung erscheint stattdessen der Dialog mit „Run it“ und „Refuse“.
Zwei Umgehungen, die durchkommen
Ein zweiter Satz Unit-Tests prüfte Umgehungen. git push -f, --force-with-lease, rm -r -f und rm -fr fängt der Mod ab. Zwei Befehle kommen durch.
| Befehl | Wirkung | Mod |
|---|---|---|
git push origin +main | Force-Push über das Plus vor dem Branch | nicht erkannt |
find build -delete | rekursives Löschen ohne rm | nicht erkannt |
Das ist kein Fehler der Mod-Schnittstelle, sondern eine Eigenschaft jedes Guards, der Befehlstext mit Mustern vergleicht. Die Doku sagt das an einer Stelle selbst und nennt solche Prüfungen eine Erinnerung für Claude. Wer Force-Pushes auf main wirklich verhindern will, schützt den Branch auf dem Git-Server. Ein Guard-Mod ist eine zusätzliche Bremse für die häufigen Fälle, keine Sicherheitsgrenze.
Volle Nutzerrechte, keine Sandbox
Ein Mod läuft ohne Sandbox mit den Rechten des Nutzers. Laut Doku kann er Dateien lesen und schreiben, Programme starten, Netzwerkanfragen stellen, Umgebungsvariablen samt API-Schlüsseln lesen, jeden Prompt und jeden Tool-Aufruf sehen und Tool-Aufrufe genehmigen, bevor der Nutzer gefragt wird. Auch die Sandbox von Claude Code hilft laut Doku nicht, denn sie isoliert nur Claudes Bash-Befehle, nicht die Prozesse eines Mods.
Eine Grenze gibt es. Den Berechtigungsdialog kann ein Mod nicht umgestalten, er kann also nicht verändern, was dort angezeigt wird. Für Unternehmen gibt es Managed Settings, mit denen sich nutzerinstallierte Mods abschalten oder auf freigegebene beschränken lassen. Für einzelne Sitzungen schaltet --safe-mode alle installierten Mods ab, die eingebauten laufen weiter.
Praktisch heißt das: Einen fremden Mod installiert man wie ein npm-Paket mit Schreibrechten auf das ganze Home-Verzeichnis. Vorher den Code lesen und claude plugin validate laufen lassen. Ein Mod, der einen Zähler anzeigt, braucht keine Netzwerkaufrufe.
Mods von Claude schreiben lassen
Für eigene Mods muss man die Schnittstelle nicht auswendig kennen. Claude Code bringt einen eingebauten Skill plugin-authoring mit, der weiß, welche Ereignisse und Methoden die installierte Version kennt. Man beschreibt den gewünschten Mod im Chat, Claude schreibt ihn in einen sitzungseigenen Ordner unter ~/.claude/dev-mods/, und nach einer Rückfrage lädt Claude Code ihn per Hot Reload, also ohne Neustart in die laufende Sitzung. Bei jeder Speicherung lädt der Mod neu und beginnt dabei mit leerem Zustand, ein Zähler steht danach wieder bei null.
Anthropic stellt im Playground-Repository drei Beispiele bereit. token-weather zeigt die Füllung des Kontextfensters als Wetterbericht über dem Prompt, blast-radius hält riskante Befehle an und zeigt, was sie verändern würden, replay-theater spielt die Dateiänderungen der letzten Runde nach.
Panels, Leisten und das Nachladen per Hot Reload habe ich hier nicht selbst geprüft, weil mein Test im nicht interaktiven Modus lief. In der VS-Code-Erweiterung und in claude -p laufen die Hooks eines Mods, gezeichnet wird dort aber nichts.
Ein Mod für Anzeige, Gedächtnis und Rückfragen
Ein Mod lohnt sich, wenn etwas sichtbar sein soll, wenn sich etwas über die Sitzung merken muss oder wenn ein Tool-Aufruf auf eine Entscheidung warten soll. Für eine einfache Sperre mit einem vorhandenen Skript reicht ein Settings-Hook, für Anweisungen ein Skill, für den Zugriff auf andere Systeme ein MCP-Server. Mods und die anderen Arten schließen sich nicht aus, ein Plugin kann alle vier enthalten.
Die Schnittstelle ist drei Tage alt. Die Doku weist darauf hin, dass sich Ereignisse und Methoden zwischen Versionen ändern können, und empfiehlt, im README des Mods die getestete Version zu nennen. Bei jedem Laden schreibt Claude Code dafür passende TypeScript-Typen in den Mod-Ordner. In meinem Test lagen sie unter .claude-plugin/types/, mit dem Vermerk “Written by Claude Code 2.1.288” in der ersten Zeile.
Quellen
- Claude Code Docs, Mods overview: code.claude.com/docs/en/plugins/mods
- Claude Code Docs, Create a mod: code.claude.com/docs/en/plugins/mods/create
- Claude Code Docs, React to events: code.claude.com/docs/en/plugins/mods/events
- Addy Osmani, Getting started with Claude Code mods (1. Oktober 2026): claude.dev/blog
- Claude Code Changelog, Version 2.1.287: github.com/anthropics/claude-code
- Eingebaute Mods, Quellcode: github.com/anthropics/claude-code/tree/main/mods
- Beispiel-Mods: github.com/anthropics/claude-code-playground
- Eigener Test: Claude Code 2.1.288, Haiku 4.5, macOS 27.2, MacBook Pro M3 Max, 4. Oktober 2026