Blog • AI / Agent
Wie setzt man Claude-Agent-Rechte um? Von Permission Policy und MCP Tool Call zum JSON-Freigabeablauf
Die Rechtekontrolle eines Claude Agent ist eine JSON-Entscheidung zwischen dem Tool Call des Modells und der eigentlichen Ausführung. Die Policy ergibt allow, deny oder ask. Ask wird ein Freigabeeintrag. Die Entscheidung geht auf dieselbe tool_use_id zurück. Fehlt die Id oder ist sie falsch, bleibt die Sitzung stehen oder derselbe Aufruf läuft zweimal.
Wie die Plattform selbst entscheidet, steht im vorherigen Artikel. Dort bedeutet auto, dass der Server permission_policy liest und evaluated_permission schreibt. Diese Bewertung ändern Sie nicht. Dieser Artikel behandelt die drei JSON-Dokumente, die Sie anschließen: Policy, Freigabe-Warteschlange und Ablehnungsrückgabe. Eigene Tools und Aufrufe, die erst im Callback sichtbar sind, laufen hier durch.
Dieser Artikel behandelt:
- An welcher Stelle der offiziellen Bewertung
canUseToolüberhaupt läuft - Felder, Standardwerte und Abgleichreihenfolge im Permission-Policy-JSON
- Wie ein MCP Tool Call erst das Schema besteht und dann eine Regel trifft
- Welche Felder ein Freigabeeintrag speichert und was Ablauf bedeutet
- Wie allow und deny zurückgeschrieben werden, damit ein Aufruf nicht zweimal läuft
Zuerst festhalten: Steht ein Tool auf allow, sieht die Warteschlange den Aufruf nicht. canUseTool feuert nicht für einen schon freigegebenen Aufruf. Unten folgen Anschlusspunkt, Policy-JSON, Warteschlange und Rückgabe.
Wo Sie an die offizielle Bewertung anschließen
Agent SDK und Claude Code prüfen Regeln in fester Reihenfolge. Siehe Agent-SDK-Rechte und Claude-Code-Rechte. Zuerst deny, dann ask, dann allow. Ein spezielleres allow hebt ein schon getroffenes deny nicht auf.
-
1
deny stoppt zuerst
Ein treffendes deny blockiert den Aufruf, auch im Modus
bypassPermissions. Ein bloßes Tool wie Bash wird schon vor diesem Schritt aus dem Kontext genommen. -
2
ask erreicht den Callback
Ein treffendes ask geht an
canUseTool, auch wenn ein Bypass-Modus an ist. IndontAskgibt es keine Nachfrage: Der Treffer wird abgelehnt, der Callback wird nicht aufgerufen. -
3
allow fragt nicht mehr
Ein treffendes allow führt das Tool aus, und
canUseToolwird nicht aufgerufen. Nur der Toolname auf der Allow-Liste wirkt gleich, außer eine ask-Regel holt den Aufruf in den Callback zurück. -
4
Nichts hat getroffen
Wenn vorher nichts getroffen hat, gibt nur der Modus
defaultden Aufruf ancanUseToolweiter.dontAsklehnt ab und ruft den Callback nicht auf.
Die Warteschlange erhält also nur Aufrufe, die noch nicht freigegeben sind. Um jeden Aufruf zu protokollieren, nutzen Sie einen PreToolUse-Hook. Er läuft vor den übrigen Prüfungen und darf erlauben, ablehnen oder Argumente ändern, hebt aber ein schon getroffenes deny oder ask nicht auf.
Alle drei Anschlüsse teilen denselben Warteschlangeneintrag. Nur die offizielle Rückgabeform ändert sich.
| Anschluss | JSON, das Sie speichern | Form an das Modell |
|---|---|---|
| Gehostetes Managed-Agents-Tool, angehalten bei ask | permission_policy |
user.tool_confirmation |
| Agent-SDK-Callback | Ihre Policy plus Warteschlangeneintrag | { behavior, updatedInput / message } |
| Eigenes Tool | Ihre Policy plus Warteschlangeneintrag | user.custom_tool_result |
Ein eigenes Tool kommt als agent.custom_tool_use und hat kein evaluated_permission. Plattformdoku: Managed-Agents-Rechtepolicies. Wenn auto ein gehostetes Tool ablehnt, kann der Client das nicht mit einem Bestätigungsereignis umstoßen. Machen Sie die Warteschlange nicht zu einem zweiten Kanal, der allow erzwingt.
Wie das Permission-Policy-JSON aussieht
Wenn Sie nur Claude Code betreiben, reichen die allow- / ask- / deny-Zeichenketten in der offiziellen settings.json. Ein Agent im Produkt braucht zusätzlich ein Dokument, das Sie versionieren, diffen und per Schema prüfen. Das folgende Dokument ist Ihr Anwendungskontrakt, kein Anthropic-Request-Body.
{
"version": 1,
"default": "ask",
"rules": [
{
"id": "deny-env",
"effect": "deny",
"tool": "Read",
"input": { "file_path": { "const": ".env" } },
"reason_code": "secret_file"
},
{
"id": "ask-pr-main",
"effect": "ask",
"tool": "mcp__github__create_pull_request",
"input": { "base": { "enum": ["main", "master"] } },
"reason_code": "protected_branch"
},
{
"id": "allow-docs-search",
"effect": "allow",
"tool": "mcp__docs__search"
}
]
}
| Feld | Bedeutung |
|---|---|
version |
Formatversion der Policy. Auf den Warteschlangeneintrag kopieren, damit ein alter Vorgang nicht mit neuen Regeln gelesen wird. |
default |
Wirkung, wenn keine Regel trifft. Im Produkt ask oder deny verwenden, nicht allow. |
effect |
deny, ask oder allow |
tool |
Voller Toolname. Ein MCP-Tool beginnt mit mcp__, dann Servername, dann Toolname. |
input |
Optional. Wenn vorhanden, müssen auch die Argumente passen. Weglassen heißt: nur der Toolname zählt. |
reason_code |
Stabiler Maschinencode. In Rückgabe und Audit. Keinen Satz als Primärschlüssel verwenden. |
Die Reihenfolge folgt der offiziellen: alle deny, dann ask, dann allow. Innerhalb eines effect gewinnt die frühere Regel in der Datei.
Wenn default auf ask steht, geht ein neues MCP-Tool ohne Regel in die Warteschlange statt in die Ausführung. Das entspricht einem gehosteten MCP-Toolset mit Standard always_ask, nur Dokument und Warteschlange gehören Ihnen.
Wie ein MCP Tool Call eine Regel trifft
Ein Modellaufruf trägt mindestens Typ, Id, Toolname und input. Die Feldnamen folgen dem tool_use-Block der Messages API. Ein MCP-Toolname verbindet Servername und Toolname.
{
"type": "tool_use",
"id": "toolu_01AbC",
"name": "mcp__github__create_pull_request",
"input": {
"base": "main",
"title": "Retry failed checks",
"head": "fix/retry"
}
}
-
1
Zuerst die Form prüfen
Prüfen Sie input mit dem JSON Schema dieses Tools. Bei Fehler: nicht einreihen, MCP nicht aufrufen, sofort deny, reason_code
schema_invalid. -
2
Dann deny, ask, allow abgleichen
Der Aufruf oben trifft
ask-pr-main, weilbaseim enum liegt. Die Dateireihenfolge gilt nur innerhalb eines effect. -
3
Nicht nur über den Toolnamen allow setzen
Eine allow-Regel ohne input-Constraint gibt auch base gleich main frei. Argumentgrenzen gehören zur Berechtigung, sie sind keine zweite Kopie des Schema.
function decide(policy, call) {
const order = ["deny", "ask", "allow"];
for (const effect of order) {
const hit = policy.rules.find(
(rule) => rule.effect === effect && matchRule(rule, call)
);
if (hit) {
return {
effect,
rule_id: hit.id,
reason_code: hit.reason_code || null
};
}
}
return { effect: policy.default, rule_id: null, reason_code: "default" };
}
function matchRule(rule, call) {
if (rule.tool !== call.name) return false;
if (!rule.input) return true;
return Object.entries(rule.input).every(([key, cond]) => {
const value = call.input?.[key];
if (Object.prototype.hasOwnProperty.call(cond, "const")) return value === cond.const;
if (Array.isArray(cond.enum)) return cond.enum.includes(value);
return false;
});
}
Pfadpräfixe brauchen eine eigene Prüfung normalisierter Pfade. Kennt die Funktion oben eine Bedingung nicht, gibt sie false zurück: Die Regel verfehlt, sie erlaubt nicht.
| JSON Schema | Permission Policy | |
|---|---|---|
| Frage | Ist die Argumentform gültig? | Darf dieser Aufruf laufen? |
| Bei Fehler | schema_invalid, und nicht einreihen |
deny / ask |
| Beispiel | base muss ein String sein |
base ist main |
Was die Freigabe-Warteschlange speichert
Ist effect ask, speichern Sie den Eintrag, bevor Sie warten. Lassen Sie das Warten nicht nur im Speicher. Startet der Prozess neu, hat der Aufruf des Modells kein Ergebnis.
{
"id": "apr_01HZXK",
"session_id": "sess_01",
"tool_use_id": "toolu_01AbC",
"tool_name": "mcp__github__create_pull_request",
"input": {
"base": "main",
"title": "Retry failed checks",
"head": "fix/retry"
},
"matched_rule": "ask-pr-main",
"policy_version": 1,
"status": "pending",
"created_at": "2026-09-23T09:00:00Z",
"expires_at": "2026-09-23T09:15:00Z"
}
tool_use_idist eindeutig. Kommt dieselbe Id erneut, geben Sie den vorhandenen Eintrag zurück und rufen MCP nicht ein zweites Mal auf.- status darf nur von
pendingnachapproved,deniedoderexpiredwechseln. Ein Endstatus ändert sich nicht. - Nach
expires_atmarkieren Sie ihn alsexpiredund schreibendenymit reason_codeapproval_expiredzurück. Nicht standardmäßig erlauben. - input unverändert speichern, damit eine prüfende Person es lesen kann. Die Policy nicht aus input laden. Das Modell kann Regeln nicht über Argumente ändern.
Die menschliche Entscheidung als eigenes Dokument speichern. Den Warteschlangeneintrag damit nicht überschreiben.
{
"approval_id": "apr_01HZXK",
"tool_use_id": "toolu_01AbC",
"decision": "deny",
"actor": "user:alex",
"reason_code": "protected_branch",
"decided_at": "2026-09-23T09:04:12Z"
}
actor kommt aus Ihrer Anmeldung. Vertrauen Sie keinem Benutzerfeld in input.
Wie deny und allow zurückgeschrieben werden
Ist die Entscheidung endgültig, schreiben Sie sie auf dieselbe tool_use_id zurück. Fehlt das, bleibt die Sitzung im Warten. Das Modell kann nicht weiter, und die nächste Runde kann den Aufruf unter einer neuen Id wieder vorschlagen.
| Ihre Entscheidung | Agent-SDK-Callback | Gehostetes Tool, angehalten bei ask | Eigenes Tool |
|---|---|---|---|
allow |
{ behavior: "allow", updatedInput } |
result: "allow" |
Nach erfolgreicher Ausführung ein Custom-Tool-Ergebnis zurückgeben |
deny |
{ behavior: "deny", message } |
result: "deny" + deny_message |
Ein als Fehler markiertes Custom-Tool-Ergebnis, einschließlich reason_code |
Siehe die offizielle Doku zur Benutzereingabe. Ein allow in TypeScript enthält updatedInput, in Python updated_input. Vor Claude Code v2.1.207 führte das Weglassen dieses Feldes zu einem Validierungsfehler und zu deny. Schicken Sie immer das ursprüngliche input mit.
{
"behavior": "deny",
"message": "Denied by rule ask-pr-main (protected_branch). Do not retry this call."
}
message und deny_message sieht das Modell. Regel-Id und reason_code angeben. Keine internen Hostnamen, Token oder privaten Notizen der prüfenden Person.
{
"tool_use_id": "toolu_01AbC",
"effect": "deny",
"rule_id": "ask-pr-main",
"reason_code": "protected_branch",
"wrote_back": true
}
MCP erst nach allow aufrufen. deny und expired rufen es nicht auf. Die Policy führt nicht zuerst aus und fragt danach eine Person.
Abgrenzung zu settings und auto
Die drei Regelsätze beantworten nicht dieselbe Frage. Führen Sie nicht drei Listen und hoffen Sie, dass sie sich von selbst decken.
| Claude Code settings | Managed Agents | Ihre Policy und Warteschlange | |
|---|---|---|---|
| Regelform | Eine Zeichenkette aus Tool und Spezifikation | always_allow / always_ask / auto |
effect plus Toolname und optionale input-Bedingung |
| Wer bewertet | Claude Code, Reihenfolge deny, ask, allow | Die Plattform. auto bewertet diesen einen Aufruf | Ihr Code, in derselben Reihenfolge wie der offizielle Ablauf |
| Wo eine Person freigibt | Eine interaktive Nachfrage oder canUseTool | user.tool_confirmation |
Ihre Warteschlange, danach auf die beiden Rückgaben links abgebildet |
| Passt zu | Lokalem Programmieren | Einem gehosteten Agent | Einem Produkt, das Vorgänge und ein Audit behalten muss |
{
"permissions": {
"allow": ["Bash(npm test *)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)", "Read(./.env.*)"]
}
}
Tools, die eine Person sehen muss, bleiben bei effect ask. Hält auto einen Aufruf für sicher, läuft er, bevor ihn jemand sieht. Details im vorherigen Artikel. Im Produkt ist Ihre Policy die Quelle. Ein exportierter Claude-Code-deny-String ist nur eine Projektion. Der Freigabestatus bleibt in der Warteschlange.
Die drei JSON-Dokumente in JSONNote prüfen
Vor dem Ausrollen Policy, Warteschlangeneintrag und Rückgabe im Arbeitsbereich prüfen. Alle drei lassen sich im Browser formatieren, validieren und vergleichen, ohne einen laufenden Agent.
-
1
Policy formatieren
Öffnen Sie die Policy in JSON formatieren. Auch Claude-Code-settings sind striktes JSON. Ein Kommentar oder ein nachgestelltes Komma wird beim nächsten Start als Fehler gemeldet.
-
2
Warteschlangeneintrag prüfen
Fügen Sie das Schema unten in das Werkzeug JSON Schema ein und danach einen Freigabeeintrag. Fehlt tool_use_id oder liegt status außerhalb des enum, schlägt die Prüfung fehl.
-
3
Geänderte Regeln vergleichen
Vor und nach einer Regeländerung mit JSON Diff prüfen, ob
askzuallowwurde. Diese Änderung wiegt mehr als der Wortlaut. -
4
Rückgabe prüfen
Mit JSON prüfen bestätigen, dass die Rückgabe gültiges JSON bleibt und
tool_use_idnoch vorhanden ist.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": [
"id",
"session_id",
"tool_use_id",
"tool_name",
"input",
"status",
"created_at",
"expires_at"
],
"properties": {
"id": { "type": "string", "minLength": 1 },
"session_id": { "type": "string", "minLength": 1 },
"tool_use_id": { "type": "string", "minLength": 1 },
"tool_name": { "type": "string", "minLength": 1 },
"input": { "type": "object" },
"matched_rule": { "type": ["string", "null"] },
"policy_version": { "type": "integer", "minimum": 1 },
"status": { "enum": ["pending", "approved", "denied", "expired"] },
"created_at": { "type": "string", "format": "date-time" },
"expires_at": { "type": "string", "format": "date-time" }
}
}
Häufige Fragen
Warum wird canUseTool nicht aufgerufen?
Eine frühere allow-Regel oder ein Modus wie acceptEdits oder bypassPermissions hat den Aufruf schon freigegeben. Der Callback sieht nur noch nicht freigegebene Aufrufe. Für jeden Aufruf nutzen Sie PreToolUse. Deny lässt sich damit nicht aufheben.
Reicht die Prüfung gegen JSON Schema?
Nein. Schema beweist nur die Form, zum Beispiel dass base ein String ist. Ob base gleich main eine Person braucht, entscheidet die Policy. Eine gültige Form kann trotzdem deny oder ask sein.
Soll das Modell nach einer Ablehnung dieselben Argumente erneut versuchen?
Nein. Die Rückgabe sagt, dieselben Argumente nicht erneut zu versuchen. Löschen Sie das Ergebnis nicht, als wäre der Aufruf nie geschehen. Ein neuer Versuch braucht eine neue tool_use_id und einen neuen Durchlauf durch die Warteschlange.
Wird ein abgelaufener Eintrag standardmäßig erlaubt?
Nein. Markieren Sie den Eintrag als expired und schreiben Sie deny mit reason_code approval_expired zurück. Ablauf ist keine Zustimmung.
Braucht man diese Warteschlange noch, wenn Managed Agents auto nutzt?
Ja, aber nur für always_ask und eigene Tools. Einen Aufruf, den auto als deny markiert hat, kann ein Bestätigungsereignis nicht umstoßen. Machen Sie aus der Warteschlange keinen Seitenweg, der allow erzwingt.
Fazit
Rechtekontrolle umzusetzen heißt drei JSON-Dokumente: die Policy, die Sie schreiben, der Warteschlangeneintrag bei ask und die Entscheidung bei endgültigem Status. Die Reihenfolge folgt der offiziellen, deny zuerst. Schema prüft nur die Form. Hat eine Person noch nicht entschieden oder ist der Vorgang abgelaufen, rufen Sie MCP nicht auf.
Direktes Fazit: Standard ist ask oder deny. Ein allow muss das Tool nennen und, wo es darauf ankommt, die Argumente. Jede tool_use_id läuft höchstens einmal und bekommt immer eine Rückgabe.
Wer ein MCP-Tool deklariert und wer es ausführt, steht in MCP-Sicherheit. Warum JSON vor der Ausführung steht, steht in der Sicherheitsgrenze.
Als Nächstes: die Form des Freigabeeintrags prüfen, dann die Policy an den Agent geben.