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:

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. 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. 2
    ask erreicht den Callback

    Ein treffendes ask geht an canUseTool, auch wenn ein Bypass-Modus an ist. In dontAsk gibt es keine Nachfrage: Der Treffer wird abgelehnt, der Callback wird nicht aufgerufen.

  3. 3
    allow fragt nicht mehr

    Ein treffendes allow führt das Tool aus, und canUseTool wird 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. 4
    Nichts hat getroffen

    Wenn vorher nichts getroffen hat, gibt nur der Modus default den Aufruf an canUseTool weiter. dontAsk lehnt 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.

Eigene Policy: default ist ask, deny schlägt ein späteres allow
{
  "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.

Ein Aufruf, der einen Pull Request gegen einen geschützten Branch öffnet
{
  "type": "tool_use",
  "id": "toolu_01AbC",
  "name": "mcp__github__create_pull_request",
  "input": {
    "base": "main",
    "title": "Retry failed checks",
    "head": "fix/retry"
  }
}
  1. 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. 2
    Dann deny, ask, allow abgleichen

    Der Aufruf oben trifft ask-pr-main, weil base im enum liegt. Die Dateireihenfolge gilt nur innerhalb eines effect.

  3. 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.

Ein Abgleich nur für Gleichheit und enum. Regeltexte nicht in eine Shell setzen.
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.

Ein offener Freigabeeintrag. tool_use_id ist eindeutig.
{
  "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"
}

Die menschliche Entscheidung als eigenes Dokument speichern. Den Warteschlangeneintrag damit nicht überschreiben.

Die Freigabeentscheidung. actor kommt aus der Anmeldung.
{
  "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.

Ein deny an das Modell. Regel-Id und reason_code angeben und sagen, dieselben Argumente nicht erneut zu versuchen.
{
  "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.

Die Audit-Zeile getrennt von der Rückgabe speichern. wrote_back erst nach erfolgreicher Rückgabe auf true setzen.
{
  "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
Claude-Code-Regeln. Das ist die offizielle settings-Form, nicht die Anwendungspolicy oben.
{
  "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. 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. 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. 3
    Geänderte Regeln vergleichen

    Vor und nach einer Regeländerung mit JSON Diff prüfen, ob ask zu allow wurde. Diese Änderung wiegt mehr als der Wortlaut.

  4. 4
    Rückgabe prüfen

    Mit JSON prüfen bestätigen, dass die Rückgabe gültiges JSON bleibt und tool_use_id noch vorhanden ist.

JSON Schema für einen Freigabeeintrag. Direkt in das Schema-Werkzeug einfügen.
{
  "$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.

Zurück zum Blog