Skip to main content

Claude Component

The <Claude> component is the core of Smithers. It executes Claude via Claude Code CLI with full tool access, database logging, and structured output support.

Basic Usage

import { Claude } from "smithers-orchestrator";

<Claude
  model="sonnet"
  maxTurns={10}
  onFinished={(result) => console.log(result.output)}
>
  Analyze this codebase and suggest improvements.
</Claude>

Props

Model Configuration

model
'opus' | 'sonnet' | 'haiku'
default:"sonnet"
The Claude model to use.
<Claude model="opus">Complex reasoning task</Claude>
<Claude model="sonnet">Standard task</Claude>
<Claude model="haiku">Quick, simple task</Claude>
maxTurns
number
Maximum number of agentic turns (tool use cycles).
<Claude maxTurns={5}>Limited iteration task</Claude>
maxTokens
number
Maximum tokens for the response.
Reserved for future implementation. Currently has no effect.
timeout
number
Timeout in milliseconds.
<Claude timeout={60000}>Task with 1 minute timeout</Claude>

System Prompt

systemPrompt
string
Custom system prompt for Claude.
<Claude systemPrompt="You are a security expert. Focus on vulnerabilities.">
  Review this code.
</Claude>

Tool Configuration

tools
Tool[]
Custom tools to provide to Claude.
Experimental. Custom tool support is functional but API may change. Use allowedTools/disallowedTools for built-in tools, or the <Sqlite> component for database access.
<Claude
  tools={[
    {
      name: "query_database",
      description: "Query the PostgreSQL database",
      inputSchema: {
        type: "object",
        properties: {
          query: { type: "string", description: "SQL query" },
        },
        required: ["query"],
      },
      execute: async (input) => {
        const result = await db.query(input.query);
        return JSON.stringify(result);
      },
    },
  ]}
>
  Query the database for statistics.
</Claude>
allowedTools
string[]
Restrict Claude to only these built-in tools.
<Claude allowedTools={["Read", "Glob", "Grep"]}>
  Research only - no edits.
</Claude>
disallowedTools
string[]
Prevent Claude from using these tools.
<Claude disallowedTools={["Bash"]}>
  No shell commands.
</Claude>
permissionMode
'default' | 'acceptEdits' | 'plan'
Permission handling mode.
<Claude permissionMode="acceptEdits">
  Auto-accept file edits.
</Claude>

Structured Output

schema
ZodSchema
Zod schema for structured output validation.
const ResultSchema = z.object({
  summary: z.string(),
  issues: z.array(z.string()),
});

<Claude schema={ResultSchema}>
  Return structured analysis.
</Claude>
validate
(result: AgentResult) => boolean | Promise<boolean>
Custom validation function. Receives the full AgentResult including output, structured, tokensUsed, etc.
<Claude 
  schema={AnalysisSchema}
  validate={(result) => result.structured?.issues.length > 0}
>
  Must find at least one issue.
</Claude>
retryOnValidationFailure
boolean
default:"false"
Retry if validation fails.
maxRetries
number
default:"3"
Maximum retries for the outer error retry loop.
schemaRetries
number
default:"2"
Maximum retries for structured output schema validation. When Claude returns output that fails Zod schema validation, it will retry up to this many times.
<Claude schema={UserSchema} schemaRetries={3}>
  Return user data in the expected format.
</Claude>

Callbacks

onFinished
(result: AgentResult) => void
Called when execution completes.
<Claude onFinished={(result) => {
  console.log(result.output);
  console.log(result.structured);  // If schema provided
  console.log(result.tokensUsed);
}}>
  Task
</Claude>
onError
(error: Error) => void
Called on execution error.
<Claude onError={(err) => console.error(err)}>
  Risky task
</Claude>
onProgress
(message: string) => void
Called with progress updates.
onStreamPart
(part: SmithersStreamPart) => void
Called with typed stream events when experimental streaming is enabled.
onToolCall
(tool: string, input: any) => void
Called when Claude uses a tool.

Stop Conditions

stopConditions
StopCondition[]
Conditions that can stop execution early.
<Claude
  stopConditions={[
    { type: "token_limit", value: 10000 },
    { type: "pattern", value: /DONE/i },
  ]}
>
  Task with stop conditions
</Claude>

Session Management

continueConversation
boolean
Continue from the previous conversation in the same execution context.
resumeSession
string
Resume a specific session by ID.

Output Configuration

outputFormat
'text' | 'json' | 'stream-json'
default:"text"
Output format for Claude CLI responses.
  • text: Plain text output (default)
  • json: Structured JSON output
  • stream-json: Streaming JSON output (required for typed streaming)
mcpConfig
string
Path to MCP (Model Context Protocol) configuration file.
<Claude mcpConfig="./mcp-config.json">
  Use the configured MCP tools.
</Claude>

Tail Log Display

tailLogCount
number
default:"10"
Number of tail log entries to display during execution.
tailLogLines
number
default:"10"
Number of lines to show per tail log entry.

Streaming

experimentalTypedStreaming
boolean
default:"false"
Enable typed V3 stream parts for Claude CLI output. When enabled and no explicit outputFormat is provided, stream-json is used automatically.
legacyLogFormat
boolean
default:"false"
Write legacy raw text logs alongside NDJSON stream logs.
recordStreamEvents
boolean
default:"true"
Record stream events to the database when reporting is enabled.

Reporting

reportingEnabled
boolean
default:"true"
Enable database reporting for this agent. When enabled, agent execution is logged to the database for observability.

AgentResult Type

interface AgentResult<T = any> {
  output: string;              // Raw text output
  structured?: T;              // Validated structured output
  tokensUsed: {
    input: number;
    output: number;
  };
  turnsUsed: number;
  stopReason: 'completed' | 'stop_condition' | 'error' | 'cancelled';
  durationMs: number;
  exitCode?: number;
  sessionId?: string;
}

Structured Output with Zod

Get typed, validated responses:
import { z } from 'zod';

const AnalysisSchema = z.object({
  summary: z.string(),
  issues: z.array(z.object({
    severity: z.enum(['low', 'medium', 'high', 'critical']),
    file: z.string(),
    description: z.string(),
  })),
  recommendations: z.array(z.string()),
});

<Claude
  model="sonnet"
  schema={AnalysisSchema}
  maxRetries={2}
  onFinished={(result) => {
    // result.structured is typed as z.infer<typeof AnalysisSchema>
    for (const issue of result.structured.issues) {
      console.log(`[${issue.severity}] ${issue.file}: ${issue.description}`);
    }
  }}
>
  Analyze this codebase for security issues.
  Return a structured analysis.
</Claude>

Tool Restrictions

Control what Claude can do:
// Research only - no modifications
<Claude allowedTools={["Read", "Glob", "Grep"]}>
  Find all files that import the auth module.
</Claude>

// Full edit access
<Claude
  allowedTools={["Read", "Edit", "Write", "Bash"]}
  permissionMode="acceptEdits"
>
  Implement the feature.
</Claude>

// No shell access
<Claude disallowedTools={["Bash"]}>
  Analyze the code (no commands).
</Claude>

Conditional Rendering

Use state to control when Claude runs:
function ConditionalClaude() {
  const { db, reactiveDb } = useSmithers();
  
  const phase = useQueryValue<string>(
    reactiveDb,
    "SELECT value FROM state WHERE key = 'phase'"
  ) ?? "research";
  
  const setPhase = (p: string) => db.state.set('phase', p);

  return (
    <SmithersProvider db={db} executionId={executionId} maxIterations={5}>
      <If condition={phase === "research"}>
        <Claude
          allowedTools={["Read", "Glob"]}
          onFinished={() => setPhase("implement")}
        >
          Research the codebase.
        </Claude>
      </If>

      <If condition={phase === "implement"}>
        <Claude
          allowedTools={["Edit", "Write"]}
          onFinished={() => setPhase("done")}
        >
          Implement the solution.
        </Claude>
      </If>
    </SmithersProvider>
  );
}

Nested Prompt Components

Compose prompts with child components:
<Claude model="sonnet">
  <Sqlite path="./data.db">
    Database contains users and orders tables.
  </Sqlite>

  Find the top 10 customers by order value.
  Export results to a CSV file.
</Claude>

Error Handling

function ResilientClaude() {
  const { db, reactiveDb } = useSmithers();
  
  const attempts = useQueryValue<number>(
    reactiveDb,
    "SELECT value FROM state WHERE key = 'attempts'"
  ) ?? 0;
  
  const error = useQueryValue<string | null>(
    reactiveDb,
    "SELECT value FROM state WHERE key = 'error'"
  ) ?? null;

  return (
    <SmithersProvider db={db} executionId={executionId} maxIterations={3}>
      <If condition={error !== null && attempts < 3}>
        <Claude
          onFinished={() => db.state.set('error', null)}
          onError={(err) => {
            db.state.set('error', err.message);
            db.state.set('attempts', attempts + 1);
          }}
        >
          Previous attempt failed: {error}
          Try a different approach.
        </Claude>
      </If>

      <If condition={error === null}>
        <Claude
          onFinished={(r) => console.log("Success:", r.output)}
          onError={(err) => db.state.set('error', err.message)}
        >
          Complete the task.
        </Claude>
      </If>
    </SmithersProvider>
  );
}

Best Practices

Prevent runaway iterations:
<Claude maxTurns={10}>...</Claude>
Follow principle of least privilege:
// Research phase - read only
<Claude allowedTools={["Read", "Glob", "Grep"]}>

// Implementation phase - edit access
<Claude allowedTools={["Read", "Edit", "Write"]}>
Validate responses with Zod:
<Claude schema={MySchema} maxRetries={2}>