@nom-ai/sdk

Connect AI tool calls to React component props.

An agent loads data into components already in your application. You define the tools, validate their inputs and outputs, and map results to props — your app keeps the model, the fetching, the authorization, and the rendering.

Get startedRead the concepts
your dashboard
ask

Show sales and orders from July 1 to July 15

Sales

$340,326

2,697 orders · Jul 1–15

status: success

Orders

status: loading

Illustration. One request reaches two components. The agent resolves the dates, calls both component tools, and each component moves through its own lifecycle — sales has rendered, orders is still loading. No page navigation, and the data never renders inside a chat bubble. The components were already there.

You define the contract

A tool declares its input and output schemas, how to execute, and how the result becomes props. Validation runs at runtime even when a provider reports strict tool calling, and again in the browser before anything reaches a component.

Full walkthrough →
const searchProducts = defineAgentTool({
  key: "search-products",
  inputSchema: z.object({ query: z.string() }),
  outputSchema: z.object({ products: z.array(productSchema) }),
  execute: ({ query }) => fetch(`/api/products?q=${query}`).then((r) => r.json()),
  mapOutput: ({ products }) =>
    products.length === 0
      ? { status: "empty" }
      : { status: "success", props: { products } },
});

Five states, not two

idle, loading, success, empty, and failure. “Matched nothing” is its own state, so you never infer it from a zero-length list.

Instances, not types

Two copies of the same component stay independently addressable. Duplicate IDs are rejected loudly rather than silently overwriting each other.

The model is not trusted

Mounting changes discoverability, not authorization. Component data reaches the model only through a projection you write.