Anthropic SDK
Route the official Anthropic Python and Node SDKs through Sentisec by changing one argument — and what your code sees when a tool call is blocked.
The official anthropic SDKs take a base_url argument. Point it at your
workspace and every message your agent sends — and every tool call the model
comes back with — goes through Sentisec first.
The swap
import anthropic
client = anthropic.Anthropic(
base_url="https://api.sentisec.ch/ws/<your-workspace-id>",
api_key="sk_sentisec_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
)
message = client.messages.create(
model="claude-sonnet-4",
max_tokens=512,
messages=[{"role": "user", "content": "Reconcile the quarterly ledger."}],
)
print(message.content)import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({
baseURL: 'https://api.sentisec.ch/ws/<your-workspace-id>',
apiKey: 'sk_sentisec_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
});
const message = await client.messages.create({
model: 'claude-sonnet-4',
max_tokens: 512,
messages: [{ role: 'user', content: 'Reconcile the quarterly ledger.' }],
});
console.log(message.content);The base URL has no /v1 suffix here — the SDK appends /v1/messages
itself, so adding your own produces /v1/v1/messages and a 404. This is
the mirror image of the OpenAI recipe,
where the /v1 does belong in the base URL. The Vercel AI SDK's
@ai-sdk/anthropic, which Mastra uses, is not this SDK: its base URL
ends in /v1. See Vercel AI SDK and Mastra.
The API key is your Sentisec key. Sentisec holds the upstream Anthropic credential, so your provider key never ships in your application config. The workspace id in the URL is what carries your policy; why it matters.
What happens when a call is blocked
The request succeeds with 200. No exception is raised and no error
status is returned. By default the blocked tool_use block becomes a
text block at the same position:
[Sentisec blocked tool_use: <why the call was stopped>]Other tool_use blocks in the message that only read are released. A
call that changes something is not sent when another call in the message
is held or refused, and it carries its own short explanation (see
calls proposed together).
stop_reason stays tool_use while an allowed call remains and is end_turn otherwise. A
stream carries the same text in place of the call's frames. Your loop
needs no special case: with no call left to run, the message is the final
answer, and the explanation reaches your user.
If your workspace turns on the rename opt-in
(how), the block instead stays a
tool_use, keeping its id, with its name and input replaced:
ToolUseBlock(type='tool_use', id='toolu_01...',
name='sentisec_tool_call_blocked',
input={'error': 'tool_call_blocked', 'reason': '...'})The SDK's tool runner (supported versions) answers it as an unknown tool, Sentisec replaces that answer with its ruling, and the model re-plans. If you dispatch tool calls yourself, check the name first:
for block in message.content:
if block.type != "tool_use":
continue
if block.name == "sentisec_tool_call_blocked":
continue # return block.input["reason"] as its tool_result; never retry it
dispatch(block)Streaming
client.messages.stream() works normally, including on a blocked call:
with client.messages.stream(
model="claude-sonnet-4",
max_tokens=512,
messages=messages,
tools=tools,
) as stream:
for text in stream.text_stream:
print(text, end="")
final = stream.get_final_message()get_final_message() returns a completed message carrying the
explanation and a standard stop reason. It does not raise.
Verdicts
Every request produces a verdict on the dashboard — NOMINAL when nothing
was unusual, escalating to HIJACK when a tool call is traced back to
content the agent was never supposed to act on. The intervention
(PROCEED, SANITIZE, HALT) records what actually happened to the call.
New workspaces start in a monitor-only posture, so you can watch what a policy would do before it does it.
See also
OpenAI SDK
Route the official OpenAI Python and Node SDKs, and the Python Agents SDK, through Sentisec by changing one argument — and what your code sees when a tool call is blocked.
LangChain
Route LangChain through Sentisec with a base-URL change, and understand exactly what a LangChain agent loop does when a tool call is blocked.