# Claude Agent SDK

The Claude Agent SDK is Claude Code as a Python or TypeScript library. To send an agent to the exchange, pass the exchange origin, your key and a cost cap in the environment of each `query()` call. Tools, hooks, subagents and sessions work as they do with Anthropic.

A `query()` call starts the Claude Code CLI as a subprocess with the environment that you give it. For direct Messages API calls, use the [Anthropic SDK](https://inference.ai.exchange/docs/connect-anthropic-sdk.md).

## Print the setup with the Liquid CLI

The [Liquid CLI](https://inference.ai.exchange/docs/cli.md) prints the setup for the Claude Agent SDK with your model filled in. To run your own program with the exchange in its environment, use `liquid exec`. It sets `OPENAI_BASE_URL`, `ANTHROPIC_BASE_URL` and the keys for that process only.

```bash
curl -fsSL https://inference.ai.exchange/cli/install.sh | bash
liquid login
liquid connect claude-agent-sdk
liquid exec -- python app.py
```

To set up the Claude Agent SDK yourself, follow the steps below.

## Before you start

- A workspace with a balance and a `spend` key. See [API keys](https://inference.ai.exchange/docs/keys.md).
- The key exported in the environment of the process that runs the agent. It then never appears in your code:

  ```bash
  export LIQUID_API_KEY=sk-liquid-...
  ```

## 1. Install the SDK

The package includes the Claude Code CLI. Do not install Claude Code separately.

```bash
# Python 3.10 or newer
pip install claude-agent-sdk

# TypeScript, Node 18 or newer
npm install @anthropic-ai/claude-agent-sdk
```

## 2. Point the agent at the exchange

Pass these options to every `query()` call. Both versions set the same variables.

**Python**

```python
import asyncio
import os

from claude_agent_sdk import ClaudeAgentOptions, query

options = ClaudeAgentOptions(
    env={
        "ANTHROPIC_API_KEY": "",
        "ANTHROPIC_AUTH_TOKEN": os.environ["LIQUID_API_KEY"],
        "ANTHROPIC_BASE_URL": "https://router.inference.ai.exchange",
        "ANTHROPIC_CUSTOM_HEADERS": "x-liquid-cap-usd: 0.50",
        "ANTHROPIC_DEFAULT_HAIKU_MODEL": "cc.anthropic/liquid.auto",
        "ANTHROPIC_MODEL": "cc.anthropic/liquid.auto",
        "CLAUDE_CODE_ATTRIBUTION_HEADER": "0",
        "CLAUDE_CODE_AUTO_MODE_SERVER": "0",
        "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "16384",
        "CLAUDE_CODE_SUBAGENT_MODEL": "cc.anthropic/liquid.auto",
    },
    setting_sources=[],
    allowed_tools=["Read", "Glob"],
)

async def main():
    async for message in query(prompt="List the files here.", options=options):
        print(message)

asyncio.run(main())
```

**TypeScript**

```typescript
import { query } from '@anthropic-ai/claude-agent-sdk'

const options = {
  env: {
    ...process.env,
    ANTHROPIC_API_KEY: '',
    ANTHROPIC_AUTH_TOKEN: process.env.LIQUID_API_KEY,
    ANTHROPIC_BASE_URL: 'https://router.inference.ai.exchange',
    ANTHROPIC_CUSTOM_HEADERS: 'x-liquid-cap-usd: 0.50',
    ANTHROPIC_DEFAULT_HAIKU_MODEL: 'cc.anthropic/liquid.auto',
    ANTHROPIC_MODEL: 'cc.anthropic/liquid.auto',
    CLAUDE_CODE_ATTRIBUTION_HEADER: '0',
    CLAUDE_CODE_AUTO_MODE_SERVER: '0',
    CLAUDE_CODE_MAX_OUTPUT_TOKENS: '16384',
    CLAUDE_CODE_SUBAGENT_MODEL: 'cc.anthropic/liquid.auto',
  },
  settingSources: [],
  allowedTools: ['Read', 'Glob'],
}

for await (const message of query({ prompt: 'List the files here.', options })) {
  console.log(message)
}
```

The [Claude Code guide](https://inference.ai.exchange/docs/connect-claude-code.md) explains each variable. These four lines matter for the SDK:

| Line                                        | Why it is set                                                                                                                                                                                                                                               |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ANTHROPIC_AUTH_TOKEN`                      | Your key, sent as a bearer token. The base URL is the origin with no `/v1`, because the CLI adds `/v1/messages` itself.                                                                                                                                     |
| `ANTHROPIC_API_KEY` set to an empty string  | The SDK passes its own environment to the CLI. An Anthropic key already in that environment would otherwise be sent to the exchange as `x-api-key`. The exchange uses the bearer token either way, but the empty value keeps your Anthropic key private.    |
| `ANTHROPIC_CUSTOM_HEADERS`                  | Caps each request at $0.50. See [Cap each request](#cap-each-request).                                                                                                                                                                                      |
| `setting_sources=[]` / `settingSources: []` | By default the CLI loads `~/.claude/settings.json` and the project's `.claude/` settings. An `env` block there replaces the values you pass, and requests can go to Anthropic or to another gateway. The empty list stops the CLI from loading those files. |

Differences between the two languages:

- In TypeScript, `env` replaces the subprocess environment. Keep `...process.env` first, so the CLI and the commands of the agent keep `PATH`, `HOME` and the rest.
- In Python, the SDK adds `env` to the inherited environment.

`setting_sources=[]` also stops the CLI from loading `CLAUDE.md` files, skills and hooks from those settings. To load the settings of one project, pass `["project"]`. Then make sure that its `.claude/settings.json` has no `env` block.

## 3. Run it

Save the Python version as `agent.py` or the TypeScript version as `agent.mts`. Run it from the shell where `LIQUID_API_KEY` is set:

```bash
python agent.py
# or
npx tsx agent.mts
```

The example allows only the `Read` and `Glob` tools. Change `allowed_tools` / `allowedTools` and the prompt for your own agent.

## 4. Check it works

Each request of the agent is a job on the exchange. Open the **Overview** or **Credits** page. The receipt of each job names the client as **Claude Agent SDK**, with the SDK version, and shows its cap.

`total_cost_usd` in the result message of the SDK is an estimate from token counts. It is not the charge of the exchange. Read the charge from the receipts.

## Choose a model

The exchange lists its models to Claude Code with a `cc.anthropic/` prefix. A model published as `liquid.auto` is `cc.anthropic/liquid.auto` here. Add `[1m]` if the live input window of the model is one million tokens or more.

The CLI asks for a different model for each type of work, with one variable for each. The example sets all three to the same model:

- `ANTHROPIC_MODEL` is the main agent.
- `ANTHROPIC_DEFAULT_HAIKU_MODEL` is background work such as session titles.
- `CLAUDE_CODE_SUBAGENT_MODEL` is the model that subagents use.

If a variable is not set, its requests ask for a `claude-*` model that the exchange does not list, and they fail.

The `model` option of `query()` sets only the model of the main agent. To send background work to `liquid.auto:economy` or to a preset, see [A different model per task](https://inference.ai.exchange/docs/connect-claude-code.md) in the Claude Code guide.

## Cap each request

The CLI builds the request body, so the SDK cannot send the `exchange` object. Send the purchasing rules as headers in `ANTHROPIC_CUSTOM_HEADERS`. Separate headers with a newline:

```python
"ANTHROPIC_CUSTOM_HEADERS": "x-liquid-cap-usd: 0.50\nx-liquid-policy: economy",
```

- `x-liquid-cap-usd` is the maximum charge for one request, in USD. An agent sends many requests for each `query()`, and each has its own cap.
- If the estimated charge of a request is more than the cap, the exchange refuses the request. It does not make the request smaller.
- If the exchange cannot parse a header, it returns `400 invalid_header`.
- `liquid.auto` has a cap of its own, and the header replaces it.

No header can set models. Presets can set models. See [Purchasing rules](https://inference.ai.exchange/docs/rules.md) for each header.

## Custom tools

Tools that you define with `@tool` and `create_sdk_mcp_server` (Python) or `tool()` and `createSdkMcpServer` (TypeScript) run in your own process. The model sees them as usual tool definitions, so they need no exchange setting. See the [Agent SDK documentation](https://code.claude.com/docs/en/agent-sdk/overview).

## Control the cost

The exchange sets a cap on each request before the first token. To limit the total spend of an agent, give it its own key with a spending limit. When the key reaches the limit, the exchange refuses more requests. See [API keys](https://inference.ai.exchange/docs/keys.md).

## Troubleshooting

| Symptom                                           | Cause and fix                                                                                                                                                                     |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Requests go to Anthropic or to another gateway.   | An `env` block in `~/.claude/settings.json` or `.claude/settings.json` is replacing your values. Pass `setting_sources=[]` / `settingSources: []`.                                |
| `KeyError: 'LIQUID_API_KEY'` in Python, or `401`. | The key is not in the environment of the process that runs the agent. Export it before you start the process. Otherwise confirm the key has the `spend` scope and is not revoked. |
| `model_not_found` or the model is rejected.       | The name is not on the book, the `cc.anthropic/` prefix is missing, or one of the three model variables is not set. Check the name against `GET /v1/models`.                      |
| `400 invalid_header`.                             | A rule in `ANTHROPIC_CUSTOM_HEADERS` cannot be parsed. See [Cap each request](#cap-each-request).                                                                                 |
| Answers stop with `stop_reason: "max_tokens"`.    | The offers' output limit is below what the CLI asked for. Lower `CLAUDE_CODE_MAX_OUTPUT_TOKENS`, or choose a model whose offers allow more.                                       |
| A request is refused for lack of funds.           | The workspace balance cannot cover the cap. Add funds on the **Credits** page.                                                                                                    |

## Next steps

- [Claude Code](https://inference.ai.exchange/docs/connect-claude-code.md): what each shared variable does.
- [Purchasing rules](https://inference.ai.exchange/docs/rules.md): models, strategy and limits, and presets.
- [API keys](https://inference.ai.exchange/docs/keys.md): set a spending limit on the key the agent uses.
- [Prompt caching](https://inference.ai.exchange/docs/caching.md): how the exchange prices cached input.
- [Anthropic SDK](https://inference.ai.exchange/docs/connect-anthropic-sdk.md): call the Messages API directly.
