Skip to main content

Ship a Claude Agent to Your Team

You built a great Claude agent locally — a system prompt, a few tools, maybe a .claude/agents/*.md. It works on your laptop. But your teammates can't use it, there's no governance, no shared traces, and no way to run it as a specific end-user with their own keys.

This tutorial publishes that local agent to Waxell as a declarative claude_agent_sdk agent. When you're done it runs on the governed isolation-runner, lives at https://<your-tenant>.waxell.dev/agents/<name>, and can resolve per-end-user credentials host-side — so the model never sees a raw secret.

Let Claude do it for you

Run wax claude-init once in your project. It installs the ship-claude-agent-to-waxell skill, so Claude Code can drive this whole tutorial for you — just say "deploy this agent to Waxell."

Prerequisites

pip install waxell                 # ships the `wax` CLI
wax login # or configure a profile (see below)
wax whoami # ALWAYS verify tenant + identity first

Pushing to prod uses a profile that points at your prod tenant, e.g. wax -p acme_prod …. Verify with wax whoami -p acme_prod. Don't assume the default profile is prod.

1. Describe the agent in waxell.yaml

A claude_agent_sdk agent on Waxell is declarative. Translate your local agent into a waxell.yaml at the root of your project:

version: 1

defaults:
framework: claude_agent_sdk
owner: you@acme.com

agents:
- name: release_notes
version: 1.0.0 # bump on every re-push
display_name: Release Notes Writer
description: Drafts release notes from a changelog.
model: claude-3-5-sonnet
tools: [Read, Glob, Grep, WebFetch]
system_prompt: |
You write crisp, user-facing release notes from a raw changelog.
Group changes by theme, lead with user impact, skip internal churn.
signals:
- name: write_release_notes
source_type: webhook
description: Draft release notes for a version.
schema:
prompt:
type: string
description: The changelog + version to summarise
max_turns: 12
max_budget_usd: 4.0
  • tools are the Claude Code built-ins (Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch, …). List only what the agent needs.
  • signals are how the agent is triggered; the schema is its input.
  • Paste your local system prompt in directly.

2. Validate and publish

wax -p acme_prod push --dry-run          # validate the manifest
wax -p acme_prod push --include-source # publish (source travels with it)
wax -p acme_prod agents info release_notes

That's it — the agent is live at https://acme.waxell.dev/agents/release_notes. Your team can see its spec, bindings, traces, cost, and governance.

3. Run it

wax -p acme_prod signals send write_release_notes \
--data '{"prompt":"v2.1: added SSO, fixed 3 billing bugs..."}'
wax -p acme_prod runs list --agent release_notes
wax -p acme_prod runs show <run_id>

The run's Setup span shows exactly what the agent was provisioned with — allowed tools, domains, identity. That's your first stop when debugging.

4. Make it per-user (optional)

The payoff of running on Waxell instead of a laptop: the same agent can run as a specific end-user, reasoning on their LLM key and calling tools with their tokens — all resolved host-side.

a) Register the end-user (maps your app's user id to a Waxell sub-user):

wax -p acme_prod end-users create --tenant-sub-user-id alice \
--email alice@acme.com --display-name "Alice"

b) Set that user's secrets (their own Anthropic key, a tool token). These are write-only and encrypted at rest — you set/rotate but never read them back:

wax -p acme_prod end-users set-secret alice ANTHROPIC_API_KEY sk-ant-…
wax -p acme_prod end-users set-secret alice GITHUB_TOKEN ghp_…
wax -p acme_prod end-users list-secrets alice # keys only, never values

c) Run as Alice — your backend stamps a signed _sub_user_identity on the signal (stamp both sub_user_id and user_id):

POST /api/v1/signals/write_release_notes/
{
"prompt": "...",
"_sub_user_identity": {"sub_user_id": "alice", "user_id": "alice", "email": "alice@acme.com"}
}

The run now reasons on Alice's key and uses Alice's tokens. Resolution precedence is SubUser → Tool → Agent → Tenant — a user's own credential overrides every shared default, and one user can never resolve another's.

Gotchas

  • Bump version: on every re-push — re-pushing the same version with changed content fails with semver_conflict.
  • Don't hardcode tool names in the prompt; the platform may expose a tool as mcp__…__<name>. Say "use the available GitHub tool."
  • wax whoami first, always — the #1 cause of "it deployed to the wrong place" is the wrong profile.

What's next

  • Build & push a custom tool — give the agent a capability the built-ins don't cover.
  • Add a per-user tool integration (a "connect your GitHub" domain) so the agent acts on third-party APIs as the end-user.
  • Wire governance policies to the agent (see the Governance tutorial).
  • Explore the Token Lab in the workspace — a live reference for the whole per-end-user credential flow.