Agent frameworks
Setu returns tool definitions in the format your model API expects. Pass format to tools.list or
tools.search: openai, anthropic or raw (plain JSON Schema).
OpenAI#
const tools = await setu.tools.search("send a slack message", { format: "openai" });
const res = await openai.chat.completions.create({ model: "gpt-4o", messages, tools });
const msg = res.choices[0].message;
if (msg.tool_calls) {
const out = await setu.handleToolCalls({ userId, format: "openai", toolCalls: msg.tool_calls });
messages.push(msg, ...out); // role: "tool" messages
}Anthropic#
const tools = await setu.tools.search("create a linear issue", { format: "anthropic" });
const res = await anthropic.messages.create({ model: "claude-sonnet-5-5", max_tokens: 1024, messages, tools });
const uses = res.content.filter((b) => b.type === "tool_use");
if (uses.length) {
const results = await setu.handleToolCalls({ userId, format: "anthropic", toolCalls: uses });
messages.push({ role: "assistant", content: res.content }, { role: "user", content: results });
}Python#
tools = setu.tools.search("create an issue", format="openai")
# ... call your model, then:
tool_messages = setu.handle_tool_calls(user_id, tool_calls)Error handling#
handleToolCalls never throws for provider problems. A failed call becomes a tool result such as
{ "error": "INVALID_INPUT", "message": "input.title is required" }so the model can fix the arguments and retry. If the error is CONNECTION_NEEDS_REAUTH, show the user a new
connect link instead of retrying.
Keep the tool list small#
Models choose better from five relevant tools than from two hundred. Use tools.search with the user's
request, restrict toolkits to what the user connected, or use the MCP dynamic mode, which does this for you.