Custom Tools
Custom tools are functions that run inside your own process and become part of Amara’s available actions. When the Syntic model decides a tool fits the task, the SDK invokes your handler, passes the validated arguments, and feeds the return value back into the conversation. This is the most direct way to teach the agent about your domain — no external server, no extra transport, just a function.
Each tool has three parts: a name the model uses to reference it, a description that tells the model when and why to reach for it, and an input schema that both documents and validates the arguments. The clarity of the description matters as much as the code — it is the model’s only guide to correct usage.
Defining a tool
Define a tool with a name, description, schema, and an async handler. The handler receives typed input and returns content for the model.
import { query, tool } from "@syntic/agent-sdk";
import { z } from "zod";
const getWeather = tool({
name: "get_weather",
description: "Return the current weather for a city.",
inputSchema: z.object({ city: z.string() }),
handler: async ({ city }) => {
const data = await fetchWeather(city);
return { content: [{ type: "text", text: `${city}: ${data.summary}` }] };
},
});
for await (const msg of query({
prompt: "What's the weather in Lisbon?",
options: { tools: [getWeather] },
})) {
if (msg.type === "result") console.log(msg.result);
}from syntic_agent_sdk import query, tool
@tool(name="get_weather", description="Return the current weather for a city.")
async def get_weather(city: str) -> str:
data = await fetch_weather(city)
return f"{city}: {data.summary}"
async for msg in query(
prompt="What's the weather in Lisbon?",
options={"tools": [get_weather]},
):
if msg.type == "result":
print(msg.result)Designing good tools
Keep each tool focused on one clear action; a narrow tool is easier for the model to use correctly than a broad one with many modes. Write descriptions in plain language that state what the tool does and when it applies, and name parameters the way a caller would think about them. Validate inputs in the schema so malformed calls fail fast with a helpful message rather than reaching your business logic. Finally, return concise, structured results — the model reads your output, so surface the signal and drop the noise.