> ## Documentation Index
> Fetch the complete documentation index at: https://docs.opper.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools

> Define functions an agent can call during its loop

<Info>
  Source: [`02_agent_with_tools.py`](https://github.com/opper-ai/opper-sdks/blob/main/python/examples/agents/02_agent_with_tools.py) · [`02-agent-with-tools.ts`](https://github.com/opper-ai/opper-sdks/blob/main/typescript/examples/agents/02-agent-with-tools.ts)
</Info>

A tool is a Python or TypeScript function the agent may decide to call. The SDK introspects parameters, sends a JSON Schema to the model, and executes the function when the model picks it.

## Define a tool

<CodeGroup>
  ```python Python theme={null}
  from opperai.agent import tool

  @tool
  def lookup_product(product_id: str) -> dict:
      """Look up a product by its ID."""
      return {"name": "Headphones", "price": 79.99, "stock": 42}
  ```

  ```typescript TypeScript theme={null}
  import { z } from "zod";
  import { tool } from "opperai";

  const lookupProduct = tool({
    name: "lookup_product",
    description: "Look up a product by its ID",
    parameters: z.object({
      product_id: z.string().describe("The product ID (e.g. prod-001)"),
    }),
    execute: async ({ product_id }) => {
      return { name: "Headphones", price: 79.99, stock: 42 };
    },
  });
  ```
</CodeGroup>

In Python, the schema comes from the type hints and the docstring becomes the description. In TypeScript, you pass an explicit Zod schema, any other [Standard Schema](https://standardschema.dev/) (Valibot, ArkType, and so on), or raw JSON Schema.

## Attach tools to an agent

<CodeGroup>
  ```python Python theme={null}
  from opperai.agent import Agent

  agent = Agent(
      name="shop-assistant",
      instructions="Use tools to look up products. Be concise.",
      tools=[lookup_product, check_availability],
  )

  result = await agent.run("Can I order 10 units of prod-002?")
  ```

  ```typescript TypeScript theme={null}
  import { Agent } from "opperai";

  const agent = new Agent({
    name: "shop-assistant",
    instructions: "Use tools to look up products. Be concise.",
    tools: [lookupProduct, checkAvailability],
  });

  const result = await agent.run("Can I order 10 units of prod-002?");
  ```
</CodeGroup>

The agent loops automatically: it calls a tool, observes the result, and either calls another tool or produces a final answer.

## Inspect tool calls

Every tool invocation is captured in `result.meta`:

<CodeGroup>
  ```python Python theme={null}
  for call in result.meta.tool_calls:
      print(call.name, call.input, "->", call.output, f"({call.duration_ms:.0f}ms)")
  ```

  ```typescript TypeScript theme={null}
  for (const call of result.meta.toolCalls) {
    console.log(call.name, call.input, "->", call.output, `(${call.durationMs}ms)`);
  }
  ```
</CodeGroup>

## Tips

* Return JSON-serializable data. Dicts, lists, and primitives work best, since the SDK serializes them for the model.
* Keep the docstring or description short. That text is what the model reads when it decides whether to call the tool.
* Errors are fine. If the function raises, the SDK reports the error to the model so it can recover or report back.
* Tool calls run in parallel by default. If the model issues several tool calls in one turn, the SDK runs them concurrently. Disable this with `parallel_tool_execution=False` (Python) or `parallelToolExecution: false` (TS) on the `Agent`.
