Claude Code
Protect a Claude Code session without changing any code — point it at a local relay instead of api.anthropic.com.
Every other integration on this site works by calling the Tawa SDK from
inside your own application. tawa-claude-proxy is different: it's a
standalone relay that sits between the Claude Code CLI and Anthropic, so you
get scanning without editing Claude Code itself or writing any integration
code at all.
Claude Code reads ANTHROPIC_BASE_URL once at startup and routes every
request through whatever's listening there — including the requests made
inside its own agentic sub-loops, not just the first one. Point that at
tawa-claude-proxy instead of api.anthropic.com directly, and every
request is scanned before it goes anywhere near Anthropic:
┌─▶ your Tawa account's hosted
│ detection (request scan)
│
Claude Code ──ANTHROPIC_BASE_URL──▶ tawa-claude-proxy ──▶ api.anthropic.com
│ │
│ (streamed response) ◀────────┘
└─▶ your Tawa account's hosted
detection (tool-call scan + model's own
reply, before either reaches Claude Code)Newer than the SDK integrations
This is a newer integration path than the TypeScript/Python SDKs, verified
end-to-end against real infrastructure (a real local-stack tawa-gateway,
real api.anthropic.com) rather than against a long-lived, real-world
Claude Code session by the Tawa team. Per-user identifiers and Policy
Studio rules aren't wired in yet — see
Scope today.
What gets scanned
Both directions of every POST /v1/messages are inspected.
Inbound, before it reaches Anthropic: the relay looks at the newest message in the transcript — Claude Code resends the whole conversation on every turn, but only ever appends one new message before its next request, so scanning just that one keeps the cost of scanning flat instead of growing with the conversation.
- If it's a fresh human turn, it's scanned as
user_prompt. - If it's a
tool_resultbeing fed back into context, it's scanned astool_output. This is the indirect-injection vector worth taking seriously: a file, webpage, or command output a tool reads can carry instructions aimed at the model, and this is what catches that before it re-enters context.
Outbound, before Claude Code (or you) can act on it: two kinds of content block in the streamed response are buffered and scanned, for two different reasons — everything else (thinking blocks, message-level events) streams through live, unbuffered.
- A proposed tool call — any
tool_useblock — is scanned via Tawa's agent tool-call route the instant it finishes streaming, before Claude Code ever sees it and can execute it. This costs no extra latency Claude Code wasn't already going to spend: it can't act on a tool call until its arguments have fully streamed in as valid JSON anyway. This is the differentiated part of the whole integration — a framework-level SDK guardrail only ever sees one model call in isolation, with no visibility into what a coding agent does with the response, because tool execution happens locally and opaquely to any SDK-level hook. This is the one place in Tawa's current integrations positioned to catch a hijacked model proposing a destructive or exfiltrating action before it runs. - The model's own visible reply — any
textblock — is scanned too, on by default. This is a different threat shape from a tool call: a hijacked model can exfiltrate something it read (a secret in a file, credentials from a tool result) by simply printing it in its answer, no tool call involved at all. By default this is scanned in streaming checkpoints (every ~400 characters of new text, the whole reply-so-far is scanned and released) rather than held until the reply finishes — closer to normal streaming, at the cost of one scan call per checkpoint instead of one per reply. Both the checkpoint size and whether output-text scanning runs at all are configurable — see Scanning the model's own reply for the full cost/delay trade-off.
Any direction: a BLOCK, HARD_BLOCK, or SANDBOX verdict stops it —
a plain Anthropic-shaped error on the request side (Claude Code never
reaches api.anthropic.com), or a protocol-level event: error SSE frame
on the response side that ends the stream before the blocked tool call or
reply ever reaches the client. Every other verdict (ALLOW, REVIEW,
SOFT_BLOCK, AMBIGUOUS, OOD) forwards through by default: there's no
human sitting in a local CLI's loop to hand a REVIEW verdict to, so only a
real block stops anything by default — and that default is itself
overridable per deployment. A REVIEW the gateway has already collapsed to
BLOCK for the app's content-type setting is a block here. See
Configuration
for how that's decided, and how to change it.
Everything not buffered forwards byte-for-byte — this proxy never re-serializes an SSE event from parsed fields, only inspects a parsed copy and forwards the original bytes.
RAG and retrieval
There's no separate "RAG" code path, deliberately. Claude Code has no
distinct retrieval concept of its own — a vector search, a grep, a web
fetch, or an actual RAG tool all surface identically as a tool_result
block re-entering context, already covered by the inbound tool_output
scanning above. A retrieval-specific MCP tool gets scanned the same way
every other tool output does, with no special-casing needed.
Scope today
What's covered:
- Every user prompt and every tool result read back into context.
- Every proposed tool call, scanned before Claude Code can execute it.
- The model's own visible reply, scanned before it reaches Claude Code (on by default, toggleable).
- Any of Claude Code's agentic sub-loops, since they all go through the same
ANTHROPIC_BASE_URLoverride.
What isn't built yet:
- Parallel/interleaved tool calls. The gate tracks multiple concurrent buffered blocks correctly (keyed by content-block index), but this is untested against a real parallel-tool-call response — Claude Code has fine-grained tool streaming off by default behind a custom base URL, so blocks are generated sequentially in the common case.
- A
/compactfalse-positive risk: a compaction request's payload doesn't fit the "scan the newest message" model cleanly. Diagnosed but not yet fixed. - Anything besides Claude Code — a Codex CLI relay is planned (it speaks OpenAI's Responses API, a different wire protocol entirely) but not built.
/v1/messages/count_tokensand/v1/models— both optional per Claude Code's own gateway docs, skipped for now.