---
title: Tools
description: defineTool — flat builder for the Tool interface. JSON schema in, async execute out. Drop into agent.tool() or pull a whole MCP server's surface via agent.tools().
---

import CodeFile from '@/components/CodeFile.astro';

> Your agent needs to look up an order. The LLM doesn't know your order database — it knows what you tell it via the tool's JSON schema. **Tools are the contract** between LLM intent ("call this with these args") and your code ("here's what happens"). Get the schema right and the LLM uses the tool well; get it wrong and you debug schema-vs-args mismatches at 2 AM.

## What a tool is

A `Tool` has two parts:

```ts
{
  schema: { name, description, inputSchema },  // what the LLM sees
  execute: async (args) => result,             // what runs when LLM calls it
}
```

The `inputSchema` is a JSON Schema describing the args the LLM should produce. It's the LLM's contract — it generates well-formed args based on that shape.

## defineTool — the flat builder

`defineTool` is a flatter helper that puts name + description + inputSchema at the top level instead of nested under `schema`:

```ts
import { defineTool } from 'agentfootprint';

const lookup = defineTool<{ orderId: string }, string>({
  name: 'lookup_order',
  description: 'Look up an order by ID',
  inputSchema: {
    type: 'object',
    properties: { orderId: { type: 'string' } },
    required: ['orderId'],
  },
  execute: async ({ orderId }) => `Order ${orderId}: shipped, $299`,
});

const agent = Agent.create({ provider }).tool(lookup).build();
```

Type parameters `<TArgs, TResult>` flow into `execute` so your handler is fully typed.

## Example — agent + tool registered inline

<CodeFile path="examples/core/02-agent-with-tools.ts" region="build" title="examples/core/02-agent-with-tools.ts (region: build)" />

`.tool(...)` accepts both shapes (flat `{schema, execute}` or `defineTool` output) — the agent doesn't care which.

## Bulk-register from MCP

Pull a whole MCP server's tool surface into the agent in one call:

<CodeFile path="examples/context-engineering/08-mcp.ts" region="connect-and-attach" title="examples/context-engineering/08-mcp.ts (region: connect-and-attach)" />

`agent.tools(arr)` is the bulk-register companion to `agent.tool(t)`. Tool-name uniqueness is validated at registration time — `.tool()` (and `.tools()`, which calls it per entry) throws `Agent.tool(): duplicate tool name '<name>'` the moment a collision is registered, so an MCP-imported tool clashing with a manually-defined one fails fast. (Collisions between static tools and Skill-injected tools are caught later, at `.build()`.)

For mock-first development of MCP integrations without spawning a subprocess, use `mockMcpClient({ tools })` — same `McpClient` interface, in-memory implementation. See [Tool discovery](/agentfootprint/guides/tool-discovery/) for the full MCP surface.

## Tool errors

When `execute` throws, the framework catches it, reports it to the LLM as a tool error, and continues the loop. The LLM sees the error message and can decide to retry with different args, try a different tool, or surface to the user. See [Error handling](/agentfootprint/guides/error-handling/) for the typed error contract + retry decorators.

## Anti-patterns

- Don't reach outside the args + `ctx` your tool is given. `execute(args, ctx)` receives the LLM-supplied args plus a `ToolExecutionContext` (`{ toolCallId, iteration, signal? }`) — use `ctx.signal` to honor cancellation, not ambient globals. (`execute` may return a value OR a Promise; the loop `await`s either, so sync handlers are fine.)
- Don't put validation logic in `execute` for things JSON Schema can express (`type`, `required`, `enum`). The LLM honors well-formed schemas; redundant runtime checks are noise.
- Don't make tool descriptions ambiguous. "Get data" is bad. "Look up an order by ID; returns status + amount" is good. The LLM picks tools by description.

## Next steps

- [Tool discovery](/agentfootprint/guides/tool-discovery/) — connect to any MCP server with `mcpClient({ transport })`
- [Tool providers](/agentfootprint/guides/tool-providers/) — dynamic per-iteration tool catalogs (`staticTools`, `gatedTools`, `skillScopedTools`)
- [Skills, explained](/agentfootprint/guides/skills-explained/) — LLM-activated tool bundles
- [Permission gating](/agentfootprint/guides/security/) — restrict which tools an agent can call
