samai-sdk
Getting started

Quick start

Three pieces — a tool, a client, and a generate call — are enough for a working tool-calling assistant. Everything else in the SDK builds on this same shape.

1. Define a tool

parameters accepts a zod schema (or any Standard Schema V1 validator like valibot) — execute()'s argument types are inferred from it automatically.

tool.ts
import { z } from "zod";

const getWeather = {
  name: "get_weather",
  description: "Get the current weather for a city",
  parameters: z.object({ city: z.string() }),
  execute: async ({ city }: { city: string }) =>
    `18C and cloudy in ${city}`,
};

2. Create a client

A client pairs a Provider with any guardrails you want applied to every call. Swap anthropic() for any of the other 7 providers later without touching anything below.

client.ts
import { createClient, anthropic } from "samai-sdk";

const client = createClient({
  provider: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }),
});

3. Generate

run.ts
const result = await client.generate({
  model: "claude-sonnet-4-6",
  system: "You are a concise assistant.",
  messages: [{ role: "user", content: "What's the weather in Chennai?" }],
  tools: [getWeather],
  maxToolRoundtrips: 2,
});

console.log(result.text);

maxToolRoundtrips caps how many times the model can call a tool and read the result back before the call returns — raise it for multi-step tool use, lower it to force a quick answer.

The agent-runtime version

client.generate() is the raw building block. defineAgent() + runAgent() is the same idea wrapped in the full runtime — reusable agent config, and access to handoffs, tracing, and streaming events for free:

agent.ts
import { createClient, anthropic, defineAgent, defineTool, runAgent } from "samai-sdk";
import { z } from "zod";

const client = createClient({
  provider: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }),
});

const getWeather = defineTool({
  name: "get_weather",
  description: "Get the current weather for a city",
  parameters: z.object({ city: z.string() }),
  execute: async ({ city }) => `18°C and cloudy in ${city}`,
});

const agent = defineAgent({
  name: "weather_agent",
  instructions: "Answer weather questions using get_weather.",
  model: "claude-sonnet-4-6",
  tools: [getWeather],
});

const result = await runAgent(client, agent, "What's the weather in Nairobi?");
console.log(result.output);

Next

Continue to Agents, handoffs & sessions to see how multiple agents delegate to each other, and how conversation history persists across separate runs.