seekrit
Docs/Claude Agent SDK

Claude Agent SDK

The Claude Agent SDK is Claude Code as a library: built-in Read, Edit, Bash, Grep. Two facts about it decide how you should hand it credentials.

First, it reads ANTHROPIC_API_KEY from the environment of the process that runs your agent, and does not load .env files — so process wrapping isn't a workaround here, it's the documented path.

Second, this agent reads files and runs commands. A .env sitting in the working directory is not inert configuration; it is inside the agent's reach, along with git log -p and docker compose config. The safest place for a credential in this framework is an environment variable in the child process and nowhere else.

note

Start here if seekrit is new: three commands put the keys in an environment, mint a token bound to it, and export SEEKRIT_TOKEN. A token reads everything in its environment, so there is no per-key or per-framework setup to do before any of the below.

1. Wrap the process

seekrit run -- python agent.py
seekrit run -- npx tsx agent.ts
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage


async def main():
    async for message in query(
        prompt="Review utils.py for bugs that would cause crashes. Fix any issues you find.",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Edit", "Glob"],
            permission_mode="acceptEdits",
        ),
    ):
        if isinstance(message, AssistantMessage):
            print(message.content)


asyncio.run(main())

Install is pip install claude-agent-sdk or npm install @anthropic-ai/claude-agent-sdk.

2. Resolve in code

import os
import seekrit

seekrit.Client().into_env()          # before the first query()
assert "ANTHROPIC_API_KEY" in os.environ

Useful when the agent runs inside a server that has no wrapper — a queue worker or a web handler. Resolve once at boot, not per request.

3. Never hold the key

The Agent SDK is built on the same foundation as Claude Code and loads the same settings, so it takes the same gateway variables and the proxy needs no code change:

export ANTHROPIC_BASE_URL=http://127.0.0.1:8080/anthropic
export ANTHROPIC_AUTH_TOKEN='{{seekrit:ANTHROPIC_API_KEY}}'
# seekrit-proxy.toml
listen = "127.0.0.1:8080"

[[route]]
prefix = "/anthropic"
upstream = "https://api.anthropic.com"
allow = ["ANTHROPIC_API_KEY"]
methods = ["POST"]
paths = ["/v1/messages"]

ANTHROPIC_AUTH_TOKEN sends the value as Authorization: Bearer …; ANTHROPIC_API_KEY sends it as x-api-key. The proxy substitutes placeholders in either header, so pick whichever your setup already uses — but use exactly one, since a credential in the variable your gateway doesn't read fails with a 401 that looks like a seekrit problem and isn't.

Why there is no in-process option here

The other pages in this section offer a fetch/httpx shim as a middle rung between an environment variable and the proxy. It does not apply here: the Claude Agent SDK drives a subprocess rather than calling the API from your process, so there is no client of yours to hand a transport to. Environment or proxy are the two options.

Gotchas

  • The agent can reach whatever the process can. With Bash allowed, an agent that wants a credential can go looking for one. That is the argument for keeping plaintext out of the filesystem entirely rather than for tightening allowed_tools — though tightening them is also worth doing.
  • Multi-tenant needs more than a credential boundary. Anthropic's own docs warn against relying on default query() options for tenant isolation: managed policy settings, ~/.claude.json, and per-directory auto memory are read regardless of settingSources. For a multi-tenant agent, give each tenant its own filesystem, pass settingSources: [] and CLAUDE_CODE_DISABLE_AUTO_MEMORY=1, and resolve that tenant's secrets per run rather than injecting every tenant's into one process.
  • A wrapped process is not a sandboxed one. seekrit run keeps values off disk; it does not stop the agent reading the environment it was given. If the agent must not be able to read the key at all, that is shape 3.
  • Authenticate with seekrit login, not an exported SEEKRIT_TOKEN. seekrit run injects the resolved values on top of the environment it inherits, so a token exported in your shell is visible to the child too — and a token is a decryption credential, so an agent holding it can resolve everything that token grants, not just what you injected. seekrit login stores it in ~/.config/seekrit/config.json instead, leaving nothing in the environment for the agent to find.