Skip to main content

Unified Python library and CLI for orchestrating coding agents (Claude Code, Codex, etc.) with MCP tool servers and credential management.

Project description

caw

Coding Agent Wrapper — a Python library and CLI for orchestrating coding agents (Claude Code, Codex, opencode) with a unified interface, MCP tool servers, and credential management for Docker containers.

Install

pip install coding-agent-wrapper

Import caw:

import caw

For local development:

pip install -e .

Requires Python 3.10+.

Library: Unified Agent Interface

caw wraps multiple coding agent CLIs behind a single Agent / Session API. Swap providers without changing your code.

Quick start

from caw import Agent

agent = Agent()  # defaults to claude_code
traj = agent.completion("Explain what this repository does")
print(traj.result)
print(f"{traj.usage.total_tokens} tokens, ${traj.usage.cost_usd:.4f}")

Multi-turn sessions

from caw import Agent

agent = Agent(provider="claude_code", model="opus", reasoning="high")
agent.set_system_prompt("You are a security reviewer.")

with agent.start_session() as session:
    turn1 = session.send("Review src/auth.py for vulnerabilities")
    print(turn1.result)

    turn2 = session.send("Now check src/api.py")
    print(turn2.result)

# session.end() called automatically, returns full Trajectory

Resuming sessions across processes

Grab a resume_handle (a string) and store it anywhere — a database, a file, a queue. Later, in a different process, resume the conversation:

# Process 1: start, communicate, persist the handle.
agent = Agent(provider="claude_code")
session = agent.start_session()
session.send("My deploy target is staging-eu. Remember that.")
handle = session.resume_handle          # store this string
session.end()

# Process 2 (later, after a restart): resume by handle.
agent = Agent(provider="claude_code")
session = agent.resume_session(handle)
print(session.send("Where am I deploying?").result)   # -> "staging-eu"
session.end()

The handle is a self-contained JSON string carrying the backend's own resume key, so resuming works even with no data_dir — the underlying CLI still has the conversation:

{"version": 1, "provider": "claude_code", "session_id": "bd260210-…", "resume_key": "bd260210-…"}

(resume_key is claude's session id, Codex's thread_id, or opencode's session id — for codex/opencode it differs from session_id.) Send at least one message before reading resume_handle; the backend assigns its key on the first exchange. Works across all three providers.

The handle grants resume access to the conversation — treat it like a secret, not an opaque random id.

data_dir is optional and additive:

without data_dir with the original data_dir
backend conversation resumed resumed
caw trajectory starts empty full history restored
new turns not persisted appended to the original session dir

Providers

Provider CLI Provider name
Claude Code claude claude_code
Codex codex codex
opencode opencode opencode

Set via constructor, environment variable, or at runtime:

agent = Agent(provider="codex")
# or
os.environ["CAW_PROVIDER"] = "codex"
# or
agent.set_provider("codex")

MCP tool servers

Attach MCP servers so the agent can call external tools:

from caw import Agent, MCPServer

agent = Agent()
agent.add_mcp_server(MCPServer(
    name="my_db",
    command="python",
    args=["-m", "my_mcp_server"],
))

ToolKit: declarative tool servers

Define tools as Python classes. caw spins up an HTTP MCP server automatically:

from caw import Agent, ToolKit, tool

class UserDB(ToolKit, server_name="user_db"):
    def __init__(self):
        self.users = ["Alice", "Bob"]

    @tool(description="List all users")
    async def list_users(self) -> str:
        return ", ".join(self.users)

    @tool(description="Add a user")
    async def add_user(self, name: str) -> str:
        self.users.append(name)
        return f"Added {name}"

db = UserDB()
agent = Agent(system_prompt="You have access to a user database.")
agent.add_tool_server(db.as_server())

traj = agent.completion("Add Eve to the user database, then list all users")

Subagents

Register child agents that the parent can invoke as tools:

from caw import Agent, AgentSpec

reviewer = AgentSpec(
    name="security_reviewer",
    description="Reviews code for security issues",
    system_prompt="You are a security expert. Review the given code.",
)

agent = Agent()
agent.add_subagent(reviewer)
traj = agent.completion("Review the auth module for vulnerabilities")

# Subagent trajectories are captured:
for sub in traj.subagent_trajectories:
    print(f"  subagent: {sub.agent}, {sub.num_turns} turns")

Data models

Every interaction produces a Trajectory with structured data:

Trajectory
├── agent, model, session_id, created_at
├── turns: list[Turn]
│   ├── input: str
│   ├── output: list[TextBlock | ThinkingBlock | ToolUse]
│   │   └── ToolUse.subagent_trajectory: Trajectory | None
│   ├── usage: UsageStats
│   └── duration_ms: int
├── usage: UsageStats (own)
└── total_usage: UsageStats (own + all nested subagents)

Sessions are persisted to JSONL in caw_data/ by default.

Environment variables

Variable Purpose
CAW_PROVIDER Default provider (claude_code, codex)
CAW_MODEL Default model name
CAW_EFFORT Default reasoning effort (high, medium, low)

CLI: caw auth — Credential Management for Docker Containers

Manages coding agent OAuth credentials so they stay in sync between your host and Docker containers. Supports Claude Code, Codex, and opencode. Host credential files are never modified — they are bind-mounted into the container at run time.

caw auth setup                        # snapshot configs, write mount manifest
caw auth status                       # token expiry, last modified, mount flags
docker run $(caw auth docker-flags) -v ./project:/work my-image
caw auth teardown                     # rm -rf ~/.caw/auth/  (host files untouched)

See caw/auth/README.md for details on how it works, container setup, and supported agents.

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

coding_agent_wrapper-0.1.5.tar.gz (78.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

coding_agent_wrapper-0.1.5-py3-none-any.whl (96.3 kB view details)

Uploaded Python 3

File details

Details for the file coding_agent_wrapper-0.1.5.tar.gz.

File metadata

  • Download URL: coding_agent_wrapper-0.1.5.tar.gz
  • Upload date:
  • Size: 78.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for coding_agent_wrapper-0.1.5.tar.gz
Algorithm Hash digest
SHA256 4973197ba4e1de9c25e25d1068c51c298a1b07030b8c7e8f83745bd1cfa50ef1
MD5 597cd7ba0308142ce114249fff81c88b
BLAKE2b-256 e8adf642d1b1d30ae4a9cc36d05e72de373ed3cc2d6a0183841e25cf6179486d

See more details on using hashes here.

Provenance

The following attestation bundles were made for coding_agent_wrapper-0.1.5.tar.gz:

Publisher: publish.yml on zzjas/caw

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file coding_agent_wrapper-0.1.5-py3-none-any.whl.

File metadata

File hashes

Hashes for coding_agent_wrapper-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 f8e76302b5fa4362601c1121247458341d99c98afd6be318f480587b9a8baf30
MD5 88c56e59a4024a659514ab7248f4364d
BLAKE2b-256 4b9b354ac00033b5d30f231e91e149edc6b58c4340fd3f625f8ad2d4d9a21ec9

See more details on using hashes here.

Provenance

The following attestation bundles were made for coding_agent_wrapper-0.1.5-py3-none-any.whl:

Publisher: publish.yml on zzjas/caw

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page