Skip to main content
This guide walks through setting up a Moru template with Claude Agent SDK pre-installed.

Prerequisites

Creating the Template

Option 1: Using the SDK

from moru import Template

template = (
    Template()
    .from_node_image("lts")
    .run_cmd("npm install -g @anthropic-ai/claude-code")
    .set_envs({
        "CLAUDE_CODE_DISABLE_TELEMETRY": "1"
    })
    .set_workdir("/home/user")
)

info = Template.build(template, alias="claude-code")
print(f"Template ID: {info.template_id}")

Option 2: Using a Dockerfile

Create a Dockerfile:
FROM node:20-slim

# Install Claude Code CLI
RUN npm install -g @anthropic-ai/claude-code

# Install common development tools
RUN apt-get update && apt-get install -y \
    git \
    curl \
    python3 \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

# Configure workspace
WORKDIR /home/user
ENV HOME=/home/user
ENV CLAUDE_CODE_DISABLE_TELEMETRY=1

# Create user directory structure
RUN mkdir -p /home/user/.claude
Build using CLI:
moru template create claude-code --dockerfile ./Dockerfile

Using the Template

Create a Sandbox

from moru import Sandbox

sandbox = Sandbox.create("claude-code", {
    "envs": {
        "ANTHROPIC_API_KEY": "sk-ant-..."
    },
    "timeout": 3600  # 1 hour
})

Run Agent Tasks

# Single task
result = sandbox.commands.run(
    "claude -p 'Create a Python Flask API with one endpoint'",
    timeout=300,
    on_stdout=lambda data: print(data, end=""),
    on_stderr=lambda data: print(f"ERR: {data}", end="")
)

print(f"Exit code: {result.exit_code}")

Interactive Mode

For interactive conversations, use background commands:
# Start Claude Code in interactive mode
handle = sandbox.commands.run(
    "claude",
    background=True,
    stdin=True,
    on_stdout=lambda data: print(data, end="")
)

# Send messages
handle.send_stdin("Create a hello world script\n")

# Wait for response, then send more
import time
time.sleep(10)
handle.send_stdin("Now add error handling\n")

# When done
handle.kill()

Configuration

Environment Variables

VariableDescription
ANTHROPIC_API_KEYYour Anthropic API key (required)
CLAUDE_CODE_DISABLE_TELEMETRYDisable telemetry (1 to disable)
CLAUDE_CODE_MAX_TOKENSMax tokens for responses

Workspace Setup

Set up a project workspace:
# Write project files
sandbox.files.write("/home/user/project/README.md", "# My Project")
sandbox.files.write("/home/user/project/requirements.txt", "flask\npytest")

# Run Claude in that directory
result = sandbox.commands.run(
    "claude -p 'Set up this project'",
    cwd="/home/user/project"
)

Best Practices

1. Set Appropriate Timeouts

Claude tasks can take time. Set generous timeouts:
sandbox = Sandbox.create("claude-code", timeout=3600)  # 1 hour
result = sandbox.commands.run("claude -p '...'", timeout=300)  # 5 minutes per task

2. Handle Long-Running Tasks

For complex tasks, use background commands:
handle = sandbox.commands.run("claude -p 'Build a complete app'", background=True)

# Check progress
while not handle.exit_code:
    print(f"Output so far: {len(handle.stdout)} chars")
    time.sleep(5)

result = handle.wait()

3. Secure API Keys

Never hardcode API keys:
import os

sandbox = Sandbox.create("claude-code", {
    "envs": {
        "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]
    }
})

4. Save Work Before Timeout

Before a sandbox times out, save important files:
# Save workspace
workspace = sandbox.files.read("/home/user/project", format="bytes")
with open("workspace_backup.tar", "wb") as f:
    sandbox.commands.run("tar -czf - /home/user/project")
    # ... save output

Next Steps