Skip to main content

Behavry Integration — Anthropic API Proxy

For teams building custom Claude integrations using the Anthropic SDK directly (rather than Claude Code or Claude Desktop), Behavry can proxy all API calls for identity verification, policy enforcement, and audit logging.


How It Works

Your Code (anthropic SDK)
↓ ANTHROPIC_BASE_URL=http://localhost:8000/api/v1/anthropic
Behavry Proxy
↓ validates JWT | audits metadata | checks OPA policy
Anthropic API (api.anthropic.com)
↑ response streamed back

The proxy:

  1. Validates your Behavry agent JWT (Authorization: Bearer <behavry-jwt>)
  2. Extracts your Anthropic API key from X-Anthropic-Key header (never logged)
  3. Audits request metadata: model, system prompt presence (boolean only — not content), input/output tokens — not message content
  4. Forwards request to https://api.anthropic.com/{path} with your key
  5. Streams response back transparently

Prerequisites

  • Behavry stack running (make dev or docker compose up)
  • A Behavry agent with web:read and web:write permissions
  • Your Anthropic API key (sk-ant-...)

Step 1 — Get a Behavry JWT

curl -s -X POST http://localhost:8000/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{"client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_SECRET", "grant_type": "client_credentials"}' \
| jq -r .access_token

Step 2 — Configure Your Code

Python (anthropic SDK)

import anthropic

BEHAVRY_JWT = "eyJhbGci..." # Behavry agent token
ANTHROPIC_KEY = "sk-ant-..." # your real Anthropic key

client = anthropic.Anthropic(
base_url="http://localhost:8000/api/v1/anthropic",
api_key=BEHAVRY_JWT, # Behavry validates this
default_headers={
"X-Anthropic-Key": ANTHROPIC_KEY, # forwarded to Anthropic, never logged
},
)

message = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
print(message.content[0].text)

Environment Variables

export ANTHROPIC_BASE_URL=http://localhost:8000/api/v1/anthropic
export ANTHROPIC_API_KEY=<behavry-jwt>
export ANTHROPIC_REAL_KEY=sk-ant-...
import os
import anthropic

client = anthropic.Anthropic(
base_url=os.environ["ANTHROPIC_BASE_URL"],
api_key=os.environ["ANTHROPIC_API_KEY"],
default_headers={"X-Anthropic-Key": os.environ["ANTHROPIC_REAL_KEY"]},
)

LangChain

from langchain_anthropic import ChatAnthropic

llm = ChatAnthropic(
anthropic_api_url="http://localhost:8000/api/v1/anthropic",
anthropic_api_key=BEHAVRY_JWT,
model="claude-opus-4-5",
model_kwargs={"extra_headers": {"X-Anthropic-Key": ANTHROPIC_KEY}},
)

Step 3 — Verify

Make a request and check http://localhost:5173Live Activity.

You should see an event with:

  • tool_name: anthropic-api
  • mcp_server: anthropic-proxy
  • action: POST
  • policy_result: allow

Audited Metadata

The proxy logs the following — message content and system prompt text are never stored:

FieldExample
Modelclaude-opus-4-5
Has system prompttrue (boolean only)
Max tokens (requested)1024
Input tokens (used)89
Output tokens234
Stop reasonend_turn

anthropic-version Header

If your client doesn't set an anthropic-version header, the proxy defaults to 2023-06-01. You can override this in your SDK or by passing the header explicitly.


Policy Control

Example OPA policy to restrict model access:

package behavry.authz

# Only allow specific Claude models for API-level access
deny if {
input.mcp_server == "anthropic-proxy"
not startswith(input.model, "claude-3-")
not startswith(input.model, "claude-opus")
}

Streaming

Streaming (stream=True) is fully supported. The proxy passes SSE chunks through without buffering. Token counts are available from Anthropic's stream final message event when using stream_manager.


Difference from Claude Code / Claude Desktop

SurfaceProtocolSetup
Claude Code CLIMCP over HTTPmcp_config.py --apply
Claude Desktop AppMCP over HTTPmcp_config.py --apply-claude-desktop
Anthropic SDK (this guide)Anthropic REST API proxyANTHROPIC_BASE_URL redirect

All three produce audit events in the same dashboard — the difference is which monitoring surface applies.


Troubleshooting

401 from Behavry

JWT expired. Re-fetch using Step 1.

401 from Anthropic (passed through)

Your X-Anthropic-Key is invalid. Verify your Anthropic API key.

anthropic-version errors

The proxy sets a default version. If you need a different version, pass anthropic-version as a header in your SDK client.