npm package: @21st-sdk/agent
Description: Define AI agents and tools with full type inference for 21st Agents
Latest version: 0.0.16

# @21st-sdk/agent

Define AI agents with full type inference. The config layer for [21st Agents](https://21st.dev/agents).

## Install

```bash
npm install @21st-sdk/agent zod
```

## Quick Start

Create `src/agent.ts`:

```ts
import { agent, tool } from "@21st-sdk/agent"
import { z } from "zod"

export default agent({
  model: "claude-sonnet-4-6",
  systemPrompt: "You are a helpful coding assistant.",
  tools: {
    greet: tool({
      description: "Greet a user by name",
      inputSchema: z.object({ name: z.string() }),
      execute: async ({ name }) => ({
        content: [{ type: "text", text: `Hello, ${name}!` }],
      }),
    }),
  },
})
```

Then deploy with the [21st SDK CLI](https://www.npmjs.com/package/@21st-sdk/cli):

```bash
npx @21st-sdk/cli login
npx @21st-sdk/cli deploy
```

## API

### `agent(config)`

Creates an agent configuration. Returns the config with a `_type: "agent"` tag for the runtime.

```ts
export default agent({
  // Model to use (default: "claude-sonnet-4-6")
  model: "claude-sonnet-4-6",

  // For "claude-code" runtime, string prompts are appended
  // to Claude Code's preset system prompt by default.
  systemPrompt: "You are a PR reviewer...",

  // Custom tools the agent can use
  tools: {
    lint: tool({ ... }),
    test: tool({ ... }),
  },

  // Runtime: "claude-code" (default) or "codex"
  runtime: "claude-code",

  // Permission mode: "default", "acceptEdits", or "bypassPermissions"
  permissionMode: "default",

  // Max turns before stopping (default: 50)
  maxTurns: 50,

  // Max budget in USD
  maxBudgetUsd: 5,

  // Lifecycle hooks
  onStart: async () => { ... },
  onToolCall: async ({ toolName, input }) => { ... },
  onToolResult: async ({ toolName, input, output }) => { ... },
  onStepFinish: async ({ step }) => { ... },
  onFinish: async ({ result }) => { ... },
  onError: async ({ error }) => { ... },
})
```

### `tool(definition)`

Creates a tool definition with Zod schema validation.

```ts
import { tool } from "@21st-sdk/agent"
import { z } from "zod"

const myTool = tool({
  description: "What this tool does",
  inputSchema: z.object({
    path: z.string(),
    verbose: z.boolean().optional(),
  }),
  execute: async (args) => {
    // args is fully typed from the schema
    return {
      content: [{ type: "text", text: "result" }],
    }
  },
})
```

### Hooks

#### `onToolCall`

Runs before a tool executes. Return `false` to block it.

```ts
onToolCall: async ({ toolName, input }) => {
  if (toolName === "Bash" && input.command?.includes("rm -rf")) {
    return false // blocked
  }
}
```

#### `onFinish`

Runs when the agent completes successfully.

```ts
onFinish: async ({ result }) => {
  await fetch("https://my-api.com/webhook", {
    method: "POST",
    body: JSON.stringify({ done: true }),
  })
}
```

## How It Works

`agent()` and `tool()` are identity functions — they return exactly what you pass in, with type inference added. The actual execution happens in the 21st Agents runtime (E2B sandbox) using the Claude Agent SDK.

Your code runs in a secure cloud sandbox with full access to Node.js, git, and system tools.

For `runtime: "claude-code"`, omitting `systemPrompt` uses Claude Code's default preset system prompt. If you pass a string, it is appended to that preset automatically. You can also pass the preset object directly:

```ts
systemPrompt: {
  type: "preset",
  preset: "claude_code",
  append: "Your extra instructions here",
}
```

## License

MIT