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

# Claude Agent SDK | AI Gateway & Observability

> Instrument the Claude Agent SDK with OpenTelemetry and route its model calls through the orq.ai AI Gateway. Capture agent traces, tool use, and cost tracking.

<CardGroup cols={2}>
  <Card title="Observability" icon="chart-line" href="#observability">
    Instrument your agents with OpenTelemetry using the SDK's hooks system to capture traces for every agent turn and tool use.
  </Card>

  <Card title={<>AI Gateway &nbsp;<Badge>Beta</Badge></>} icon="arrow-right-arrow-left" href="#ai-gateway">
    Route your Claude calls through the **AI Gateway** with a single base URL change. Zero vendor lock-in: always run on the best model at the lowest cost for your use case.
  </Card>
</CardGroup>

## Observability

### Overview

The Claude Agent SDK (`claude-agent-sdk`) drives the Claude CLI as a programmable agent supporting multi-turn conversations, tool use, and MCP servers. Use the SDK's built-in `hooks` system to attach OpenTelemetry instrumentation at key execution points. Every tool invocation becomes a span, and the full agent workflow is captured as a trace in **orq.ai** without modifying your agent logic.

### Key Benefits

<CardGroup cols={2}>
  <Card title="Full Agent Visibility" icon="chart-line">
    Track every agent turn, tool use, and LLM call with detailed traces and analytics
  </Card>

  <Card title="Zero Code Changes" icon="wand-magic-sparkles">
    Attach observability through hooks without modifying your agent logic
  </Card>

  <Card title="Cost Tracking" icon="chart-pie">
    Real-time cost and token usage per agent run synced to Orq.ai
  </Card>

  <Card title="Tool Inspection" icon="wrench">
    Inspect every tool input and output with full execution context
  </Card>
</CardGroup>

### Prerequisites

* An Orq.ai account and [API Key](/docs/ai-studio/organization/api-keys)
* Python 3.10 or higher, or Node.js 18 or higher
* [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code/getting-started) installed and authenticated

<Info>
  To set up your API key, see [API keys & Endpoints](/docs/ai-studio/organization/api-keys).
</Info>

### Install Dependencies

<Tabs>
  <Tab title="TypeScript">
    <CodeGroup>
      ```bash npm theme={"theme":{"light":"github-light","dark":"github-dark"}}
      npm install @anthropic-ai/claude-agent-sdk \
                  @arizeai/openinference-instrumentation-claude-agent-sdk \
                  @opentelemetry/sdk-node \
                  @opentelemetry/exporter-trace-otlp-http
      ```

      ```bash pnpm theme={"theme":{"light":"github-light","dark":"github-dark"}}
      pnpm add @anthropic-ai/claude-agent-sdk \
               @arizeai/openinference-instrumentation-claude-agent-sdk \
               @opentelemetry/sdk-node \
               @opentelemetry/exporter-trace-otlp-http
      ```

      ```bash bun theme={"theme":{"light":"github-light","dark":"github-dark"}}
      bun add @anthropic-ai/claude-agent-sdk \
              @arizeai/openinference-instrumentation-claude-agent-sdk \
              @opentelemetry/sdk-node \
              @opentelemetry/exporter-trace-otlp-http
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Python">
    <CodeGroup>
      ```bash uv theme={"theme":{"light":"github-light","dark":"github-dark"}}
      uv add claude-agent-sdk openinference-instrumentation-claude-agent-sdk \
             opentelemetry-sdk opentelemetry-exporter-otlp
      ```

      ```bash pip theme={"theme":{"light":"github-light","dark":"github-dark"}}
      pip install claude-agent-sdk openinference-instrumentation-claude-agent-sdk \
                  opentelemetry-sdk opentelemetry-exporter-otlp
      ```
    </CodeGroup>
  </Tab>
</Tabs>

### Configuration

Set up the OTel tracer provider to export spans to Orq.ai:

<Tabs>
  <Tab title="TypeScript">
    ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { NodeSDK } from '@opentelemetry/sdk-node';
    import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
    import { ClaudeAgentSDKInstrumentation } from '@arizeai/openinference-instrumentation-claude-agent-sdk';
    import * as ClaudeAgentSDKModule from '@anthropic-ai/claude-agent-sdk';

    const ClaudeAgentSDK = { ...ClaudeAgentSDKModule };
    const instrumentation = new ClaudeAgentSDKInstrumentation();
    instrumentation.manuallyInstrument(ClaudeAgentSDK);

    const sdk = new NodeSDK({
      traceExporter: new OTLPTraceExporter({
        url: 'https://api.orq.ai/v2/otel/v1/traces',
        headers: {
          Authorization: `Bearer ${process.env.ORQ_API_KEY}`,
        },
      }),
      instrumentations: [instrumentation],
    });

    sdk.start();
    ```
  </Tab>

  <Tab title="Python">
    ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import os
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
    from opentelemetry import trace
    from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor

    tracer_provider = TracerProvider()
    tracer_provider.add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(
                endpoint="https://api.orq.ai/v2/otel/v1/traces",
                headers={"Authorization": f"Bearer {os.environ['ORQ_API_KEY']}"},
            )
        )
    )
    trace.set_tracer_provider(tracer_provider)

    ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer_provider)
    tracer = trace.get_tracer(__name__)
    ```
  </Tab>
</Tabs>

### Basic Example

<Tabs>
  <Tab title="TypeScript">
    Spread the imported SDK module into a mutable namespace, then call `ClaudeAgentSDKInstrumentation.manuallyInstrument(...)` before `sdk.start()`. The instrumentation patches every `query()` call, tool use, and subagent into spans automatically.

    ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { NodeSDK } from '@opentelemetry/sdk-node';
    import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
    import { ClaudeAgentSDKInstrumentation } from '@arizeai/openinference-instrumentation-claude-agent-sdk';
    import * as ClaudeAgentSDKModule from '@anthropic-ai/claude-agent-sdk';

    const ClaudeAgentSDK = { ...ClaudeAgentSDKModule };
    const instrumentation = new ClaudeAgentSDKInstrumentation();
    instrumentation.manuallyInstrument(ClaudeAgentSDK);

    const sdk = new NodeSDK({
      traceExporter: new OTLPTraceExporter({
        url: 'https://api.orq.ai/v2/otel/v1/traces',
        headers: {
          Authorization: `Bearer ${process.env.ORQ_API_KEY}`,
        },
      }),
      instrumentations: [instrumentation],
    });

    sdk.start();

    const prompt = 'What is the capital of France?';
    console.log(`User: ${prompt}\n`);

    for await (const message of ClaudeAgentSDK.query({ prompt })) {
      if ('result' in message && typeof message.result === 'string') {
        console.log(`Assistant: ${message.result}`);
      }
    }

    await sdk.shutdown();
    console.log('\nTraces exported to orq.ai. View them at https://my.orq.ai');
    ```
  </Tab>

  <Tab title="Python">
    Call `ClaudeAgentSDKInstrumentor().instrument()` before any agent call. The instrumentor wraps every tool invocation, LLM turn, and subagent in spans automatically. Drive the agent via `ClaudeSDKClient` (instead of the standalone `query()` generator) and wrap the call in a root `agent-workflow` span to stamp the prompt and the final cost.

    ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import asyncio
    import os
    from claude_agent_sdk import (
        ClaudeAgentOptions,
        ClaudeSDKClient,
        AssistantMessage,
        TextBlock,
        ResultMessage,
    )
    from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
    from opentelemetry import trace

    tracer_provider = TracerProvider()
    tracer_provider.add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(
                endpoint="https://api.orq.ai/v2/otel/v1/traces",
                headers={"Authorization": f"Bearer {os.environ['ORQ_API_KEY']}"},
            )
        )
    )
    trace.set_tracer_provider(tracer_provider)

    ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer_provider)
    tracer = trace.get_tracer(__name__)


    async def main():
        options = ClaudeAgentOptions(
            model="sonnet",
            system_prompt="You are a helpful coding assistant.",
            max_turns=3,
            allowed_tools=["Read", "Bash"],
        )

        prompt = "What Python version is installed on this system?"
        print(f"User: {prompt}\n")

        with tracer.start_as_current_span("agent-workflow") as span:
            span.set_attribute("agent.prompt", prompt)

            async with ClaudeSDKClient(options=options) as client:
                await client.query(prompt)
                async for message in client.receive_response():
                    if isinstance(message, AssistantMessage):
                        for block in message.content:
                            if isinstance(block, TextBlock):
                                print(f"Assistant: {block.text}")
                    elif isinstance(message, ResultMessage):
                        span.set_attribute("agent.total_cost_usd", message.total_cost_usd or 0)
                        span.set_attribute("agent.num_turns", message.num_turns)
                        print(f"\nCost: ${message.total_cost_usd or 0:.6f} | Turns: {message.num_turns}")

        tracer_provider.force_flush()
        print("\nTraces exported to orq.ai. View them at https://my.orq.ai")


    asyncio.run(main())
    ```
  </Tab>
</Tabs>

### View Traces

View your traces in the [AI Studio](/docs/ai-studio/observability/quickstart) in the **Traces** tab.

<Info>
  Visit your [AI Studio](https://my.orq.ai) to view real-time analytics and traces.
</Info>

## AI Gateway

<Warning>
  This feature is in Beta. Traces are not supported while using the AI Gateway with the Claude Agent SDK.
</Warning>

### Overview

Route the Claude Agent SDK's model calls through **Orq.ai**'s **AI Gateway** without changing any agent logic. The SDK drives the Claude CLI, which reads `ANTHROPIC_BASE_URL` and `ANTHROPIC_AUTH_TOKEN` from the environment, so pointing those variables at the gateway routes every turn through **Orq.ai**.

<Info>
  For the raw Claude Messages API without the agent runtime, see the [Anthropic SDK](/docs/ai-studio/integrations/frameworks/anthropic) integration.
</Info>

### Configuration

Set the following environment variables before running the agent:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
export ANTHROPIC_BASE_URL="https://api.orq.ai/v3/anthropic"
export ANTHROPIC_AUTH_TOKEN="$ORQ_API_KEY"
export ANTHROPIC_API_KEY=""  # set empty so the SDK does not call the Anthropic API directly
export ANTHROPIC_MODEL="anthropic/claude-sonnet-4-6"  # the anthropic/ prefix is required
```

### Basic Example

With the environment variables set, every agent run routes through the **AI Gateway** with no change to the calling code:

<Tabs>
  <Tab title="TypeScript">
    ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { query } from '@anthropic-ai/claude-agent-sdk';

    const prompt = 'What is the capital of France?';

    for await (const message of query({ prompt })) {
      if ('result' in message && typeof message.result === 'string') {
        console.log(message.result);
      }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import asyncio
    from claude_agent_sdk import (
        ClaudeSDKClient,
        AssistantMessage,
        TextBlock,
    )


    async def main():
        async with ClaudeSDKClient() as client:
            await client.query("What is the capital of France?")
            async for message in client.receive_response():
                if isinstance(message, AssistantMessage):
                    for block in message.content:
                        if isinstance(block, TextBlock):
                            print(block.text)


    asyncio.run(main())
    ```
  </Tab>
</Tabs>

## Other Integrations

<CardGroup cols={2}>
  <Card title="Claude Code" icon="terminal" href="/docs/ai-studio/integrations/code-assistants/claude-code">
    Route Claude Code through the AI Gateway using environment variables. Integrate Claude Code with our MCP to access 20+ tools and actions on the platform.
  </Card>

  <Card title="Claude Desktop" icon="desktop" href="/docs/ai-studio/integrations/code-assistants/claude-desktop">
    Connect your Orq.ai workspace to Claude Desktop using the MCP integration.
  </Card>
</CardGroup>
