> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adrian.secureagentics.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Anthropic SDK

> Reference for Adrian's Anthropic SDK instrumentation: install, usage, invocations, streaming, and enforcement.

Adrian instruments the [Anthropic SDK](https://docs.anthropic.com/) directly, for agents that call `messages.create` rather than going through a framework. Every call on both `anthropic.Anthropic` and `anthropic.AsyncAnthropic` is captured as a `PairedEvent` and streamed to the Adrian backend. Your call sites stay unchanged.

This ships inside the [Python SDK](/reference/sdk) rather than as a separate package, so configuration, callbacks, and the `PairedEvent` schema are shared with the LangChain integration and documented there.

## Install

```sh theme={null}
pip install "adrian-sdk[anthropic]"
```

Requires Python 3.12+. The extra pins a supported `anthropic` version. Plain `pip install adrian-sdk` also works, since the instrumentation patches whichever `anthropic` your project already depends on. If the package is absent, Adrian skips Anthropic patching and everything else continues as normal.

## Initialise

`init` and `shutdown` bracket your normal Anthropic code.

```python theme={null}
import asyncio
import os

import adrian
import anthropic


async def main():
    adrian.init(api_key="adr_live_...", ws_url="wss://adrian.secureagentics.ai/ws")

    # Your Anthropic code runs normally, and every call is captured.
    client = anthropic.AsyncAnthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

    async with adrian.anthropic_invocation():
        response = await client.messages.create(
            model="claude-opus-5",
            max_tokens=1024,
            system="You are a helpful assistant.",
            messages=[{"role": "user", "content": "What is 2 + 2?"}],
        )
        # Thinking blocks can precede the text block, so select by type.
        print(next(b.text for b in response.content if b.type == "text"))

    adrian.shutdown()


asyncio.run(main())
```

For synchronous code use `adrian.anthropic_invocation_sync()`.

## Grouping related calls

An invocation is Adrian's unit of work. A single Anthropic call is not one, so wrap related calls to group them under a shared `invocation_id`.

```python theme={null}
async with adrian.anthropic_invocation():
    first = await client.messages.create(...)
    second = await client.messages.create(...)   # same invocation_id
```

Calls made outside an invocation are still captured, but carry `invocation_id="no_invocation"` and cannot be correlated with each other.

## Streaming

Text deltas stream through untouched. The event is emitted when the final message is requested.

```python theme={null}
async with adrian.anthropic_invocation():
    async with client.messages.stream(
        model="claude-opus-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Count to five."}],
    ) as stream:
        async for text in stream.text_stream:
            print(text, end="", flush=True)

        message = await stream.get_final_message()   # emitted and gated here
```

## Enforcement modes

The agent profile's execution mode is set in the dashboard and pushed to the SDK in the `LoginAck` frame. See [Severity codes](/reference/severity-codes) for what each MAD code means.

| Mode         | Behaviour                                                                                                |
| ------------ | -------------------------------------------------------------------------------------------------------- |
| Alert        | The response passes through unchanged. Events are classified and surfaced in the dashboard.              |
| Block        | Each `tool_use` block in the response waits on its verdict before the response is returned to your code. |
| Human Review | As Block, but the verdict waits on a dashboard decision instead of timing out.                           |

Under Block and Human Review, a halted tool call never reaches your execution loop. Halted blocks are rewritten to a text block reading `[BLOCKED by security policy]`, and `stop_reason` is downgraded from `tool_use` to `end_turn` so agentic loops terminate cleanly.

<Note>
  The gate fails closed. If no `LoginAck` arrives within 5s all tool calls are blocked, and in Block mode a verdict timeout blocks the tool call.
</Note>

## Manual instrumentation

`init()` patches the Anthropic SDK automatically. To control when that happens:

```python theme={null}
adrian.init(api_key="adr_live_...", auto_instrument=False)
adrian.patch_anthropic()
```

Patching is idempotent and safe to call more than once.

## What's captured

Each call produces one `PairedEvent` with `pair_type="llm"`. `LlmPairData` carries the model, the flattened message list including the system prompt, the output text, requested `tool_calls`, and token usage. Content blocks are normalised to strings, so a `tool_use` block appears as `[tool_use: name args={...}]` in the message text.

Agent identity is derived from the system prompt, since the Anthropic SDK exposes no framework-level agent boundary. Two agents sharing a system prompt share an `agent_id`.
