---
title: "Claude Code - Auto Mode on Non-Claude Models"
url: "/docs/claude_code_auto_mode"
canonical_url: "https://docs.litellm.ai/docs/claude_code_auto_mode"
type: "docs"
last_updated: "2026-10-14"
summary: "LiteLLM answers Claude Code's auto mode safety checks for models that cannot answer them on their own."
related:
  - "/docs/claude_code_context_management"
  - "/docs/tutorials/claude_desktop_cowork"
---
# Claude Code - Auto Mode on Non-Claude Models

> Index of all LiteLLM docs: https://docs.litellm.ai/llms.txt


LiteLLM answers Claude Code's auto mode safety checks for models that cannot answer them on their own.

Claude Code's auto mode runs tool calls without asking the user first. When `ANTHROPIC_BASE_URL` points at a gateway, Claude Code v2.1.278 or later sends `safeguards: [{"type": "dangerous_tool_use", ...}]` on every `POST /v1/messages` and expects `safeguard_results` back, with a verdict for each tool call in the response. The Anthropic API answers that itself. When the proxy routes `/v1/messages` to a non-Claude model nothing answered, so `/status` showed `Auto mode server: Disabled` after the first reply and Claude Code sent its own classifier requests for the rest of the session. Anthropic's [permission modes reference](https://code.claude.com/docs/en/permission-modes#server-side-classifier-review) describes that fallback, and on accounts where those requests are billed Claude Code shows a [notice about classifier request charges](https://code.claude.com/docs/en/auto-mode-classifier-billing).

With `safeguards_classifier_model` set, the proxy asks a deployment from your `model_list` to judge each tool call and returns the verdicts the same way the Anthropic API does, so auto mode stays on server review when Claude Code is pointed at an open model.

Available from v1.107.0.

| Model behind `/v1/messages` | Who answers `safeguards` |
|---|---|
| `anthropic/` Claude | Anthropic API |
| `bedrock/` Claude (InvokeModel) | Amazon Bedrock |
| `bedrock_mantle/` Claude | Amazon Bedrock |
| `vertex_ai/` Claude | Vertex AI |
| `azure_ai/` Claude | LiteLLM forwards to Foundry's own `/v1/messages` and does not classify |
| `bedrock/converse/` models, Claude included | LiteLLM classifier |
| Fireworks, Gemini, and other chat completions providers | LiteLLM classifier |
| `openai/` models | LiteLLM classifier |
| DeepSeek and other providers with their own `/v1/messages` | LiteLLM classifier |

The LiteLLM classifier answers for every non-Claude model, whichever way the proxy reaches it: the chat completions translation, the Responses API bridge that `openai/` deployments use, or a provider's own Anthropic-compatible `/v1/messages`, such as DeepSeek's. It also answers for Claude models behind `bedrock/converse/`, since that route goes through the chat completions translation. Claude models that LiteLLM forwards to a provider's own `/v1/messages` are left to that provider, and when any provider returns its own `safeguard_results`, the proxy keeps them and skips the classifier call.

Requests without `safeguards`, which is every client other than Claude Code in auto mode, are untouched.

## Setup

Add `safeguards_classifier_model` to `general_settings`, naming a `model_name` from your `model_list`. The setting is off by default. The name must be one the proxy can route, since a name with no deployment behind it makes every check come back `unsupported` (see [When the Classifier Cannot Be Used](#when-the-classifier-cannot-be-used)):

```yaml
model_list:
  - model_name: kimi-k3
    litellm_params:
      model: fireworks_ai/accounts/fireworks/models/kimi-k3
      api_key: os.environ/FIREWORKS_AI_API_KEY
  - model_name: gpt-oss-120b
    litellm_params:
      model: fireworks_ai/accounts/fireworks/models/gpt-oss-120b
      api_key: os.environ/FIREWORKS_AI_API_KEY

general_settings:
  safeguards_classifier_model: gpt-oss-120b
```

The classifier runs on every turn that ends in a tool call, so pick a fast, cheap model that reliably answers in JSON. An open model on the same provider as the coding model works well. The example above uses one Fireworks deployment for coding and another for the verdicts.

The classifier call goes through the router like any other request, so `model_list` aliases, fallbacks, and cooldowns apply to it. It is never retried. The proxy waits at most 30 seconds for its answer, and a shorter `timeout` under the classifier deployment's `litellm_params` ends the wait earlier.

Every virtual key that runs Claude Code needs access to the classifier model. Before the classifier call, the proxy runs the same model access checks it would run if the caller asked for that model directly, for the key, team, team member, user, project, end user, and agent. It then checks the overall and per-model budgets of the key, team, user, team member, and end user, the caller's TPM headroom, and any RPM limit set on the classifier model itself, without using any of it. As on a direct call, a classifier model priced at zero skips the budget checks. The overall RPM limits of the key, team, and user are left out of that check, since the Claude Code request already counted against them. The classifier call's tokens count toward the caller's TPM and spend, but the call takes no request slot, so it adds nothing to any RPM count. A refused access check or a spent budget turns the classifier off for the session (see [When the Classifier Cannot Be Used](#when-the-classifier-cannot-be-used)). A full RPM or TPM window, or a check the proxy could not run, only skips the classifier for that one response (see [When the Classifier Fails](#when-the-classifier-fails)).

## How It Works

On each `/v1/messages` request that carries `safeguards` and goes to a model the LiteLLM classifier answers for, LiteLLM sends the request to the backend as before. Once the response is in, it sends one chat completions call to the classifier deployment with:

- the tool calls of the response, exactly as the model wrote them
- the conversation so far, including earlier tool calls and their results
- the agent's environment from the request: permission mode, platform, working directory, trusted directories, allow and deny rules, and git branch

The classifier flags a tool call that does something dangerous the user did not ask for, such as sending credentials somewhere, piping a URL into a shell, writing outside the trusted directories, deleting data that cannot be regenerated, or following instructions it found in a file or a tool result. Routine development work inside the trusted directories, and an action the user explicitly asked for, is not flagged.

LiteLLM returns the verdicts as `safeguard_results`, on the response for non-streaming calls and inside the final `message_delta` event for streaming calls. Claude Code blocks a flagged call and runs an unflagged one. A reply with no tool calls gets an empty verdict set without a classifier call.

The transcript is built from the messages Claude Code sent, before any compaction the proxy applies, and a compaction summary from Claude Code shows up as one entry. To keep the classifier call small, the transcript is trimmed to about 24,000 characters. The user's opening message is kept whole when it fits in half of that. A longer one is cut from its start, since Claude Code puts the user's request after its reminders at the end of that message. Later turns fill the rest, newest first. Each transcript entry is clipped to 2,000 characters and each tool result to 500. The pending tool calls are never clipped. The agent's system prompt and thinking blocks are not sent.

:::caution

The classifier deployment sees the conversation, tool results included. If a tool prints a secret, such as a `cat .env`, the first 500 characters of that output reach the classifier model, so pick a deployment you trust with the same data as the coding model.

:::

## Verify

Point Claude Code at the proxy with a virtual key and the open model, start the session in auto mode (see Anthropic's [permission modes reference](https://code.claude.com/docs/en/permission-modes)), and send a prompt that needs a tool call:

```bash
ANTHROPIC_BASE_URL=http://localhost:4000 ANTHROPIC_AUTH_TOKEN=<virtual key> ANTHROPIC_MODEL=kimi-k3 claude --permission-mode auto
```

After the reply, run `/status` inside the session. The Auto mode server row reads `Enabled`. Before the first reply the row reads `Enabled` on any proxy, so check it after a reply. On a route with no verdicts the same row reads `Disabled`.

To see the verdicts without Claude Code, send a request with `safeguards` to the proxy:

```bash
curl http://localhost:4000/v1/messages \
  -H "Authorization: Bearer <virtual key>" \
  -H "content-type: application/json" \
  -d '{
    "model": "kimi-k3",
    "max_tokens": 512,
    "tools": [{"name": "Bash", "description": "Run a shell command", "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}}],
    "messages": [{"role": "user", "content": "List the files in the current directory with ls -la"}],
    "safeguards": [{"type": "dangerous_tool_use", "classifier_context": {"v": 1, "permission_mode": "auto", "live_cwd": "/tmp/sandbox", "trusted_directories": ["/tmp/sandbox"]}}]
  }'
```

## Responses

### Non-streaming

```json
{
  "id": "chatcmpl-b149e0ae231645bda5c447266c30cb3e",
  "type": "message",
  "role": "assistant",
  "model": "kimi-k3",
  "content": [
    {"type": "tool_use", "id": "call_501f1ce0021b4d119f57555e", "name": "Bash", "input": {"command": "ls -la"}}
  ],
  "stop_reason": "tool_use",
  "usage": {"input_tokens": 210, "output_tokens": 73},
  "safeguard_results": [
    {
      "type": "dangerous_tool_use",
      "status": {
        "type": "available",
        "tool_uses": {
          "call_501f1ce0021b4d119f57555e": {"type": "evaluated", "outcome": "not_flagged"}
        }
      }
    }
  ]
}
```

A flagged tool call carries the classifier's reason in one sentence:

```json
{"type": "evaluated", "outcome": "flagged", "explanation": "Force-pushes to origin and exfiltrates private SSH key, actions not requested by user."}
```

Claude Code skips a flagged tool call and shows the reason in place of its output, for example `Denied by auto mode classifier ∙ Uploads a secret deploy_key to an external server, which is disallowed without explicit user request.` The model sees the denial and carries on without that action.

### Streaming

`safeguard_results` arrives inside the `delta` of the final `message_delta` event, which is where the Anthropic API puts it. LiteLLM holds that event until the classifier answers, so each turn that ends in a tool call takes as long as the classifier call on top of the model's reply:

```
event: message_delta
data: {"type": "message_delta", "delta": {"stop_reason": "tool_use", "safeguard_results": [{"type": "dangerous_tool_use", "status": {"type": "available", "tool_uses": {"call_5e0931e0bef84a3d84162c33": {"type": "evaluated", "outcome": "not_flagged"}}}}]}, "usage": {"output_tokens": 82}}

event: message_stop
data: {"type": "message_stop"}
```

## When the Classifier Fails

If the classifier call fails, the proxy marks every tool call of that response `unavailable` instead of guessing, and the agent's own reply still goes out:

```json
{"type": "unavailable", "reason": "timeout"}
```

| `reason` | When |
|---|---|
| `timeout` | No answer within 30 seconds, or within the deployment's `timeout` when that is shorter |
| `input_too_long` | Over the classifier's context window |
| `refused` | Content policy refusal |
| `truncated` | Tool call was cut off |
| `error` | Any other failure |

A tool call is cut off when it is the last block of a reply that stopped at `max_tokens`, or when its streamed arguments are not valid JSON. The proxy never sends it to the classifier, and the other tool calls of that response still get verdicts. `error` also covers a classifier reply that is not the JSON the proxy asked for. When the reply is JSON but leaves out one of the tool calls, only that tool call gets `unavailable` with `error`, and the others keep their verdicts.

A `safeguard_results` with `unavailable` verdicts still counts as server review, so `/status` keeps reading `Auto mode server: Enabled` and the session does not switch to the local fallback for good. What Claude Code does with the action depends on its version. Anthropic's [errors reference](https://code.claude.com/docs/en/errors#the-server-returned-no-safety-verdict) says auto mode denies an action the server gives no verdict for, and stops the turn after ten responses in a row without one. In our test with Claude Code v2.1.296 and a classifier that always failed with `error`, Claude Code checked each such action with its own classifier request instead, sent through the proxy (to Claude Sonnet 5 first, then to the session's model when the proxy had no Sonnet 5 deployment), and ran the actions that check allowed. Reads and edits inside the working directory never go to a classifier.

The proxy also skips the classifier call and marks every tool call of the response `unavailable` with `error` when the caller's RPM or TPM window for the classifier model is full, or when the proxy could not run one of the caller's checks (a team membership or rate-limit counter it could not read). The next response runs the checks again, so the verdicts come back once the window turns over. The log line for this case says `is over the caller's rate limit for this window, or the limit could not be read`.

When tool calls keep coming back `unavailable`, look for `safeguards classifier` warnings in the proxy logs.

## When the Classifier Cannot Be Used

When the caller has no access to the classifier model, has spent a budget that covers it, or the proxy has no deployment for the model the setting names, the proxy returns no per-tool verdicts:

```json
"safeguard_results": [{"type": "dangerous_tool_use", "status": {"type": "unsupported"}}]
```

Claude Code treats this the same as a route with no verdicts. It shows its classifier billing notice, sends its own classifier requests through the proxy for the rest of the session, and `/status` reads `Auto mode server: Disabled` after the first reply that carries a tool call, since a reply without tool calls skips the check and still counts as server review. Give the key access to the classifier model, raise the limit it hit, or point the setting at a `model_name` in `model_list`, and start a new session. A missing access or a spent budget logs `safeguards classifier <name> is outside the caller's model access or budget`, and a name with no deployment logs `safeguards classifier <name> is not served by this proxy`, on every request.

## Cost and Logs

The classifier call is a normal proxy call. It shows up as its own request on the Logs page and in spend logs, billed to the same virtual key, team, and end user as the Claude Code request that triggered it. It is logged under the same settings as that request, so per-request callback parameters and a message redaction header such as `x-litellm-enable-message-redaction` apply to its row too. Its cost adds to every turn that ends in a tool call, which is why the choice of classifier model matters.

## Related pages

- [Claude Code - Context Management](https://docs.litellm.ai/docs/claude_code_context_management.md)
- [Claude Desktop (Cowork)](https://docs.litellm.ai/docs/tutorials/claude_desktop_cowork.md)
