# How to Define Custom Actions with defineAction and Validate Them with Zod in Agent-Native

> Learn to define custom actions with defineAction in Agent-Native. Validate inputs and outputs seamlessly using Zod schemas for robust server-side functions.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: how-to-guide
- Published: 2026-07-02

---

**Use `defineAction` from `@agent-native/core` to declare server-side functions, passing a Zod schema to the `schema` property for automatic input validation and an optional `outputSchema` for return-value validation.**

Agent-Native, the open-source framework from BuilderIO for building AI-powered applications, provides a declarative API for exposing server-side functions to both UI components and AI agents. The `defineAction` helper in [`packages/core/src/action.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/action.ts) serves as the central factory for creating type-safe actions with built-in Zod validation, ensuring runtime safety and consistent JSON Schema generation for Claude tool descriptions.

## Understanding the defineAction API Structure

The `defineAction` factory function builds an `ActionDefinition` object that encapsulates metadata, validation logic, and the implementation. According to the source code in [`packages/core/src/action.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/action.ts) (starting at line 607), the options object accepts several key properties:

### Core Properties

- **`description`** – Human-readable text displayed in the UI and embedded in the Claude tool description.
- **`schema`** – A **Zod** object describing the input shape. When provided, the runtime automatically validates payloads before executing the `run` function.
- **`outputSchema`** *(optional)* – A Zod schema validating the returned data. If the action returns invalid data, the framework responds based on the `outputErrorStrategy` setting.
- **`run`** – The actual implementation receiving a fully-typed argument object (inferred from the Zod schema) that may be async.
- **`http` / `readOnly` / `toolCallable` / `agentTool`** – Metadata flags governing HTTP exposure, UI permissions, and agent-tool bridge visibility.

## How Validation Works Under the Hood

Agent-Native wraps your `run` function with validation layers when schemas are present.

### Input Validation

If `options.schema` is provided and contains the `"~standard"` marker (indicating a Standard Schema), the factory creates an input-validated wrapper using `wrapWithValidation` (around line 630 in [`action.ts`](https://github.com/BuilderIO/agent-native/blob/main/action.ts)). This wrapper executes `schema.safeParse` on incoming payloads. Validation errors are transformed into clear messages that include the expected signature, preventing malformed data from reaching your business logic.

### Output Validation

When `options.outputSchema` is supplied, a second wrapper (`wrapWithOutputValidation`) validates the return value. The `outputErrorStrategy` parameter (`strict`, `warn`, or `fallback`) determines whether the framework throws an error, logs a warning, or replaces the result with `options.outputFallback`.

The complete flow is implemented in [`packages/core/src/action.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/action.ts) between lines 635-660:

```tsx
const inputValidatedRun = hasSchema
  ? wrapWithValidation(options.schema, options.run, toolParameters)
  : options.run;

const run = hasOutputSchema
  ? wrapWithOutputValidation(
      options.outputSchema,
      inputValidatedRun,
      outputErrorStrategy,
      options.outputFallback,
      options.description,
    )
  : inputValidatedRun;

```

## Creating a Zero-Input Action

Some actions, such as retrieving the current application state, require no parameters. The `view-screen` action in [`templates/videos/actions/view-screen.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/videos/actions/view-screen.ts) demonstrates this pattern using an empty Zod object:

```tsx
import { defineAction } from "@agent-native/core";
import { readAppState } from "@agent-native/core/application-state";
import { z } from "zod";

export default defineAction({
  description:
    "See what the user is currently looking at on screen. Returns the current view and composition details.",
  schema: z.object({}),               // No input parameters required
  http: false,                        // Internal tool, not exposed as HTTP endpoint
  run: async () => {
    const navigation = await readAppState("navigation");
    const screen: Record<string, unknown> = {};

    if (navigation) screen.navigation = navigation;
    const nav = navigation as any;

    if (nav?.compositionId) {
      screen.context = {
        view: "composition",
        compositionId: nav.compositionId,
        folderId: nav.folderId ?? null,
        folderName: nav.folderName ?? null,
        hint: "User is editing a composition",
      };
    } else {
      screen.context = {
        view: "studio-home",
        hint: "User is on the studio home page",
      };
    }

    return Object.keys(screen).length === 0
      ? "No application state found. Is the app running?"
      : JSON.stringify(screen, null, 2);
  },
});

```

This action validates that no extraneous inputs are provided while returning a JSON string describing the current UI state.

## Creating Validated Actions with Input and Output Schemas

For mutations requiring strict type safety, define both input and output schemas. The following example illustrates a folder creation action with validation:

```tsx
import { defineAction } from "@agent-native/core";
import { z } from "zod";

export default defineAction({
  description: "Create a new folder in the user's library.",
  schema: z.object({
    name: z.string().min(1, "Folder name cannot be empty"),
    parentId: z.string().optional(),
  }),
  outputSchema: z.object({
    folderId: z.string(),
  }),
  http: { method: "POST", path: "/api/folder" },
  run: async ({ name, parentId }) => {
    const folderId = await createFolderInDb(name, parentId);
    return { folderId };
  },
});

```

**Execution flow for this action:**

1. The request body is parsed and validated against the input Zod schema.
2. If validation passes, the `run` function executes with fully typed arguments.
3. The returned object `{ folderId }` is validated against `outputSchema`.
4. Any output violation triggers the configured `outputErrorStrategy` (defaulting to `warn`).

## Advanced Configuration and Metadata

Beyond validation, `defineAction` supports additional flags to control exposure and behavior:

- **`http`** – Configures the action as an HTTP endpoint (e.g., `{ method: "POST", path: "/api/action" }`) or disables HTTP exposure with `false`.
- **`readOnly`** – Automatically inferred from the HTTP method when not explicitly set, affecting UI permission hints.
- **`toolCallable`** and **`agentTool`** – Boolean flags controlling whether the action appears in the agent's tool catalogue.
- **`audit`** – Hooks for logging and observability integrated into the action lifecycle.

These properties are processed alongside validation wrappers in the `defineAction` implementation to generate the final action definition consumed by the Agent-Native runtime.

## Summary

- **`defineAction`** in [`packages/core/src/action.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/action.ts) (line 607+) is the central factory for creating type-safe, validated actions in Agent-Native.
- Supply a **Zod schema** to the `schema` property to enable automatic input validation before your `run` function executes.
- Use **`outputSchema`** with `outputErrorStrategy` to validate return values and handle mismatches via strict errors, warnings, or fallback values.
- The framework uses **`wrapWithValidation`** and **`wrapWithOutputValidation`** to inject Zod's `safeParse` logic, converting validation errors into agent-friendly messages.
- Actions can be internal tools (`http: false`) or HTTP endpoints, with metadata flags controlling visibility to the AI agent and UI.

## Frequently Asked Questions

### What happens if Zod validation fails in defineAction?

When input validation fails, the wrapping function catches the Zod error and returns a clear message indicating the expected schema shape, preventing the `run` function from executing with malformed data. Output validation failures are handled according to the `outputErrorStrategy` setting (`strict`, `warn`, or `fallback`).

### Can I use output validation without input validation in Agent-Native?

Yes. While the `schema` property is optional, you can provide `outputSchema` alone to validate return values even if the action accepts no parameters or performs manual input handling. The wrappers are applied independently based on the presence of each schema.

### How does defineAction generate JSON Schema for Claude tools?

When you provide a Zod schema, `defineAction` detects the Standard Schema marker (`"~standard"`) and converts the Zod object to JSON Schema format. This ensures the AI agent receives the exact input contract, with descriptions and types matching your TypeScript definitions.

### Is defineAction only for creating HTTP endpoints?

No. By setting `http: false`, you create internal actions accessible only to the UI and AI agent without exposing a public HTTP route. This is useful for state-reading operations or sensitive utilities that should remain within the application boundary.