Anthropic SDK
Use the official Anthropic SDKs for Python and TypeScript with XField: messages, streaming, thinking, tools, and how errors map to SDK classes.
XField serves the Anthropic Messages API, so the official anthropic SDKs work with a different base URL and your key. Thinking comes back as real thinking blocks and tools use the native tool_use format.
Install and configure
pip install anthropic # Python
npm install @anthropic-ai/sdk # TypeScript / Nodeimport os
import anthropic
client = anthropic.Anthropic(
base_url="https://xfield.cloud.brutal.ai/api",
api_key=os.environ["XFIELD_API_KEY"],
)Use https://xfield.cloud.brutal.ai/api, not the /v1 URL: the SDK appends /v1/messages itself. The key goes out as x-api-key.
Messages
Models that think by default return a thinking block before the text, so pick the text block by type rather than taking content[0].
msg = client.messages.create(
model="deepseek-v4.1-flash-1017-uncensored",
max_tokens=2048,
system="You are a terse security reviewer.",
messages=[{"role": "user", "content": "Explain TOCTOU in two sentences."}],
)
print(next(b.text for b in msg.content if b.type == "text"))Streaming
with client.messages.stream(
model="deepseek-v4.1-flash-1017-uncensored",
max_tokens=2048,
messages=[{"role": "user", "content": "Draft a lab setup for testing EDR evasion detections."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
print("\n", final.usage)Thinking
Control thinking with thinking (enabled, adaptive or disabled) and depth with output_config.effort. budget_tokens is accepted for compatibility but is not enforced as a separate budget; use effort instead.
msg = client.messages.create(
model="glm-5.3-uncensored",
max_tokens=16000,
thinking={"type": "adaptive"},
output_config={"effort": "high"},
messages=[{"role": "user", "content": "Rank these three findings by exploitability and say why."}],
)
for block in msg.content:
if block.type == "thinking":
print("[thinking]", block.thinking[:200])
elif block.type == "text":
print(block.text)If your SDK version rejects output_config as an unknown argument, pass it through extra_body (Python) or upgrade. Accepted effort words are low, medium, high, xhigh and max; each model maps them to the levels it has.
Tools
tools = [{
"name": "run_query",
"description": "Run a read-only SQL query against the test database.",
"input_schema": {
"type": "object",
"properties": {"sql": {"type": "string"}},
"required": ["sql"],
},
}]
msg = client.messages.create(
model="deepseek-v4.1-flash-1017-uncensored",
max_tokens=2048,
tools=tools,
messages=[{"role": "user", "content": "How many users have no password set?"}],
)
if msg.stop_reason == "tool_use":
call = next(b for b in msg.content if b.type == "tool_use")
print(call.name, call.input)Send the whole assistant content back, then a user message with a tool_result block carrying tool_use_id=call.id, and call again. Keep the thinking blocks in the history when you do.
Errors
Errors on /messages use Anthropic's shape, so the SDK raises its usual classes. Names are the same in both SDKs.
| Status | SDK class | What it means here |
|---|---|---|
| 400 | BadRequestError | Malformed request or unknown model id |
| 401 | AuthenticationError | Key missing, unknown, disabled or expired |
| 403 | PermissionDeniedError | Account disabled, or content policy not yet confirmed |
| 415 | APIStatusError | Body was not JSON (the SDK always sends JSON) |
| 429 | RateLimitError | Rate limited; back off and retry |
| 5xx | InternalServerError | Upstream trouble; a 503 with Retry-After is safe to retry |
| none | APIConnectionError, APITimeoutError | Network failure or timeout |
The SDKs already retry 429s, 5xx responses and connection errors twice with backoff. Raise that with max_retries (Python) or maxRetries (TypeScript) on the client, and set timeout generously for long thinking runs. See limits and errors for the full list.