# Tools for AI agents: names, descriptions and results a model uses well

> Tools for AI agents are functions a model can ask your code to run, and the model knows each one only by its name, description, parameters and what it returns. Build a few tools around whole tasks, not one per API endpoint. Describe each as you would to a new hire, return short readable results and useful errors, and check every change on real tasks.

Mindket editors · Published 2026-11-06 · Updated 2026-11-06 · https://mindket.com/learn/harness-engineering/tools-for-ai-agents

## In short

- Sketch the job a person would do with your API, then offer that job as one call.
- Treat every field a tool sends back as text the model must read and pay for.
- Let a failed call teach the next one: name the bad input and show one that works.
- Keep a fixed set of hard, multi-step tasks aside and rerun them after each change to a tool.

## What a model knows about your tool

The harness sketch in lesson 1 borrowed its tools from Anthropic's [customer service cookbook](https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/customer_service_agent.ipynb), whose two stand-in functions follow as published. Read them the way a model would. Order O1 has shipped, yet `cancel_order("O1")` returns `True`, and that one word is the whole message. A model that gets it back has nothing to tell the customer except that the order is cancelled.

The cookbook wrote these to show the API, not to run a shop. In your own agent the same shape is a bug, because a model meets a tool only through four things: its name, its description, its input schema and whatever comes back. Nothing else reaches it. Ken Aizawa's [Writing effective tools for agents](https://www.anthropic.com/engineering/writing-tools-for-agents), on Anthropic's engineering blog in September 2025, calls a tool "a contract between deterministic systems and non-deterministic agents". Ask an agent "Should I bring an umbrella today?" and it might call the weather tool, answer from general knowledge or first ask where you are.

How a call and its result travel between your code and the model is covered in the [Claude tool use lesson](https://mindket.com/learn/claude-api/claude-tool-use) and, for OpenAI, the [function calling lesson](https://mindket.com/learn/openai-api/openai-function-calling). What goes inside those blocks decides whether the agent gets the task right.

````python
def get_order_details(order_id):
    # Simulated order data
    orders = {
        "O1": {
            "id": "O1",
            "product": "Widget A",
            "quantity": 2,
            "price": 19.99,
            "status": "Shipped",
        },
        "O2": {
            "id": "O2",
            "product": "Gadget B",
            "quantity": 1,
            "price": 49.99,
            "status": "Processing",
        },
    }
    return orders.get(order_id, "Order not found")


def cancel_order(order_id):
    # Simulated order cancellation
    if order_id in ["O1", "O2"]:
        return True
    else:
        return False
````

## Build tools around tasks, not API endpoints

Wrapping each API endpoint as a tool is the obvious start, and the tools post calls it a common error. Context is the reason. Software can page through a contact list one record at a time at almost no cost. A model reading a `list_contacts` result pays for every contact in tokens, inside a context window with a hard limit. A `search_contacts` or `message_contact` tool jumps straight to the right entry, the way a person turns to the right letter of an address book. Those are the two tools the post would build.

One tool can also make several API calls inside it. Anthropic gives three such merges, listed in the table, and each folds steps a person would think of as one job into a single call. Fewer, larger tools also leave fewer descriptions in the context and fewer chances to pick the wrong one.

Names start to matter once tools from several MCP servers sit side by side. The post recommends namespacing by service, as in `asana_search` and `jira_search`, and by resource, as in `asana_projects_search` and `asana_users_search`. Whether the shared part went first or last changed Anthropic's evaluation results by a non-trivial amount, and the better choice varied by model.

| Instead of | Build | What the tool does |
| --- | --- | --- |
| `list_users`, `list_events` and `create_event` | `schedule_event` | Finds a time everyone is free and books the event |
| `read_logs` | `search_logs` | Returns only the relevant log lines, with some context around each |
| `get_customer_by_id`, `list_transactions` and `list_notes` | `get_customer_context` | Gathers a customer's recent, relevant information in one result |

## Write the description for a new hire

When Anthropic launched Claude's web search tool, Claude kept adding "2025" to its search queries, which skewed the results. A better tool description fixed it. Anthropic also credits precise refinements to tool descriptions for Claude Sonnet 3.5 reaching a state-of-the-art score on SWE-bench Verified. The model reads every description on every request, so a description works like a prompt.

OpenAI's [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) reaches the same place with its intern test: could a person use the function correctly from nothing but what the model was given? The [function calling lesson](https://mindket.com/learn/openai-api/openai-function-calling) works through that test with the guide's advice on enums and on arguments your code already knows.

Bin Wu's [advanced tool use post](https://www.anthropic.com/engineering/advanced-tool-use), from November 2025, sets a good and a bad definition side by side. The bad one names a database operation and describes nothing. The good one names the task, says what it can search by and says what comes back.

````json
// Good
{
  "name": "search_customer_orders",
  "description": "Search for customer orders by date range, status, or total amount. Returns order details including items, shipping, and payment info."
}

// Bad
{
  "name": "query_db_orders",
  "description": "Execute order query"
}
````

1. Name the task, not the operation. `search_customer_orders` tells a model when to reach for it, and `query_db_orders` does not.
2. Write for someone in their first week on your team. Spell out the query formats, niche terms and links between resources that your team knows without saying.
3. Name each parameter so it cannot be misread. The tools post's example is `user_id` in place of `user`.
4. Say what comes back, and say when the tool is the wrong choice. OpenAI's guide puts that "when not to" guidance in the system prompt.
5. Add examples for tricky inputs. In Anthropic's internal testing, tool use examples raised accuracy on complex parameter handling from 72% to 90%.

## What a tool sends back

This rewrite of the cookbook's `cancel_order` is the lesson's own code. It was not run. Its description says when to call it and what returns. The function hands back a sentence the model can pass on to the customer, and on failure an error that says why and what to do next.

To try it in lesson 1's loop, change the one line that the comment at the end of the code names. The pair the new `cancel_order` returns then sets the tool result's `is_error` flag, and `get_order_details` still works. Claude then sees the refusal for O1 marked as a failed call.

````python
# The cookbook's two orders, trimmed to the fields this tool needs
ORDERS = {
    "O1": {"product": "Widget A", "quantity": 2, "status": "Shipped"},
    "O2": {"product": "Gadget B", "quantity": 1, "status": "Processing"},
}

CANCEL_ORDER = {
    "name": "cancel_order",
    "description": (
        "Cancel one order that has not shipped yet. Call it only after the customer "
        "has asked to cancel. Returns one sentence saying the order is cancelled, or "
        "an error saying why it could not be cancelled and what to do instead."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "order_id": {
                "type": "string",
                "description": "The order ID exactly as the customer or get_order_details gives it, such as O1.",
            }
        },
        "required": ["order_id"],
    },
}


def cancel_order(order_id):
    """Returns the text the model reads and whether it is an error."""
    order = ORDERS.get(order_id)
    if order is None:
        return f"No order has the ID {order_id!r}. Order IDs look like O1. Ask the customer to check it.", True
    if order["status"] == "Shipped":
        return (
            f"Order {order_id} ({order['product']}) has already shipped and cannot be cancelled. "
            "Tell the customer. Do not call cancel_order again for this order."
        ), True
    order["status"] = "Cancelled"
    return f"Order {order_id} ({order['quantity']} x {order['product']}) is now cancelled.", False


# Lesson 1's loop has the line "output, failed = fn(**block.input), False".
# Replace it with these two lines, so the pair above sets is_error and get_order_details still works:
#     result = fn(**block.input)
#     output, failed = result if isinstance(result, tuple) else (result, False)
````

- Return what the model can act on. The tools post advises against low-level identifiers such as `uuid`, `256px_image_url` and `mime_type`, in favor of `name`, `image_url` and `file_type`. Swapping random IDs for readable names, or even for IDs counted from 0, cut hallucinations and made Claude more precise in retrieval tasks.
- Let the model choose the detail. A `response_format` parameter with `"concise"` and `"detailed"` values keeps IDs out until a follow-up call needs them. In the post's Slack example, the detailed reply took 206 tokens and the concise one 72, about a third.
- Cap the size. Claude Code limits a tool response to 25,000 tokens by default. Add pagination, filters or truncation with sensible defaults, and when you truncate, say how to ask for less, such as several narrow searches in place of one broad one.
- Write errors for the model. An opaque error code or a traceback teaches it nothing. A message that names the bad input and shows a correct one steers the next call.
- Pick the result format by evaluation. JSON, XML and Markdown can each come out ahead on a given task, and the post found no single format that suits every task and agent.

## How many tools fit in one request

Every definition sits in the context on every request, whether the model uses it or not. [What is context engineering](https://mindket.com/learn/context-engineering/what-is-context-engineering) counted a five-server MCP setup at 58 tools and about 55K tokens before the conversation starts. Accuracy suffers too. Anthropic's advanced tool use post names the wrong tool and wrong parameters as the most common failures, especially between look-alikes such as `notification-send-user` and `notification-send-channel`.

OpenAI's guide gives a soft target of fewer than 20 functions available at the start of a turn. For many functions or large schemas, it pairs function calling with tool search, which defers rarely used tools until the model needs them. Only `gpt-5.4` and later models support it. The [just-in-time context lesson](https://mindket.com/learn/context-engineering/just-in-time-context) shows how deferred tools and tool search work. With Claude's Tool Search Tool, deferred tools stay out of the context until Claude searches for them.

The post suggests tool search once definitions pass 10K tokens or you have ten or more tools, with your three to five most-used tools always loaded. The just-in-time context lesson gives its token savings and accuracy figures.

## Check tools on tasks with a known answer

A new description or a merged tool is a guess until it is measured. The tools post starts from tasks grounded in real use, each paired with an outcome a script can check, from an exact string match up to Claude judging the answer. Strong tasks need several tool calls, sometimes dozens. Weak ones hand the model the exact query, so they check the search and not the tool design.

Run each task in a plain loop like lesson 1's, one task per loop, with your tools and nothing else. Track more than the score: the post lists the runtime of each call and task, the number of calls, total tokens and tool errors. Many repeated calls hint that pagination or size limits need tuning. Many invalid-parameter errors point at a description.

Then read the transcripts, because what a model leaves out of its own account can matter more than what it says. Anthropic pasted its evaluation transcripts into Claude Code and let it refactor the tools, and most of the post's advice came out of that loop. Held-out tasks kept the gains honest. On them, the refined tools beat versions written by Anthropic's researchers and versions Claude had generated.

Building a first test set from real traces is the subject of the [AI evals course](https://mindket.com/learn/ai-evals/what-are-llm-evals) later in this path, and its [agent evaluation lesson](https://mindket.com/learn/ai-evals/evaluate-ai-agents) grades whole runs like these. Start with three strong tasks and three weak ones, such as the post's own.

| Strong task | Weak task |
| --- | --- |
| Schedule a meeting with Jane next week to discuss our latest Acme Corp project. Attach the notes from our last project planning meeting and reserve a conference room. | Schedule a meeting with jane@acme.corp next week. |
| Customer ID 9182 reported that they were charged three times for a single purchase attempt. Find all relevant log entries and determine if any other customers were affected by the same issue. | Search the payment logs for purchase_complete and customer_id=9182. |
| Customer Sarah Chen just submitted a cancellation request. Prepare a retention offer. Determine: (1) why they're leaving, (2) what retention offer would be most compelling, and (3) any risk factors we should be aware of before making an offer. | Find the cancellation request by Customer ID 45892. |

## Ryan Lopopolo on quiet tools and agents that list what they lack

Ryan Lopopolo wrote OpenAI's harness engineering post, the one behind [lesson 1](https://mindket.com/learn/harness-engineering/what-is-an-agent-harness), and is now a principal engineer at Google Cloud. On 7 March 2026 he [posted on X](https://x.com/_lopopolo/status/2030357237731037528) that tools like prettier and pnpm are built for people to read by default. His fixes were to pass `--silent` to prettier and to have an agent write a wrapper, so a test run across the pnpm workspace prints only the failures. That is a concise response format for command-line tools, and any agent with a shell tool can use it.

On 26 March, in a [reply on X](https://x.com/_lopopolo/status/2037307669464363433), he offered a system prompt line for finding gaps. It asks the agent to note its mistakes in MISTAKES.md, to write down any context or tools it wishes it had in DESIRES.md and to record what it learns about its environment in LEARNINGS.md. The files are then collected as telemetry. For tool design, DESIRES.md is the one to read: each entry is the agent's own note on a tool or a piece of context it lacked.

Anthropic's video "Building with MCP and the Claude API", posted on 9 October 2025, puts Alex Albert of Claude Relations with John Welsh of the MCP team and Michael Cohen of the Claude API team. John Welsh is one of the MCP engineers credited in the tools post. The chapter at 14:20 is about managing context and tools with MCP.

> tools like prettier and pnpm are optimized for human legibility by default. Pass --silent to prettier.
>
> Ryan Lopopolo, Author of OpenAI's harness engineering post, now a principal engineer at Google Cloud, [X](https://x.com/_lopopolo/status/2030357237731037528) (read 2026-11-06)

[![Ryan Lopopolo (@_lopopolo) on X, 7 March 2026: tools like prettier and pnpm are optimized for human legibility by default. Pass --silent to prettier, and have a wrapper filter a pnpm workspace test run down to the failures.](https://mindket.com/blog/shots/x-lopopolo-silent-tool-output.webp?v=b0850f90ab5a)](https://x.com/_lopopolo/status/2030357237731037528)

Ryan Lopopolo on X, 7 March 2026

[![Ryan Lopopolo (@_lopopolo) on X, 26 March 2026, in a reply: put this in the system prompt and collect the files as telemetry. Note mistakes in MISTAKES.md, wished-for context or tools in DESIRES.md and what you learn about the environment in LEARNINGS.md.](https://mindket.com/blog/shots/x-lopopolo-desires-md.webp?v=b30d99c7cf65)](https://x.com/_lopopolo/status/2037307669464363433)

Ryan Lopopolo on X, 26 March 2026

[Building with MCP and the Claude API](https://www.youtube.com/watch?v=aZLr962R6Ag)

Alex Albert, John Welsh and Michael Cohen of Anthropic on building with MCP and the Claude API, with a chapter on managing context and tools.

> Screenshots of X's own embeds of the posts, taken on 6 November 2026, with the time in UTC. X cuts the second post short with Show more. Select one to open the post on X.

## Common mistakes

- Wrapping every API endpoint as its own tool and leaving the model to chain them.
- Returning UUIDs and every field the API has, when the model needs a name and a status.
- Returning `True`, `False` or a bare error code, which leaves the model nothing to tell the user.
- Two tools with near-identical names and no line in either description saying which one to use when.
- Loading every tool from every MCP server into every request.
- Judging a new description on a few hand-picked prompts, with no held-out tasks.

## Try it yourself

[Long-running agents](https://mindket.com/learn/harness-engineering/long-running-agents), the next lesson, keeps one agent working across many context windows with a progress file, git and tests.

- Put the rewritten `cancel_order` and its `CANCEL_ORDER` definition into lesson 1's loop, using the change from "What a tool sends back". Ask it to cancel O1, then O2, then O3, and write down what the model tells the user each time.
- Take one tool you already have and rewrite its description for a new hire. Count the facts you had to add.
- Write three strong and three weak tasks for your own tools, each with an answer a script can check.
- Add the DESIRES.md line to an agent's system prompt for a week and read what it asks for.

## Check yourself

1. An agent needs to find one person in a large address book. Which tool does Anthropic's tools post suggest?

   A. list_contacts, which returns every contact
   B. get_contact_by_uuid
   C. search_contacts

   Answer: C. A search tool jumps to the right entry, while a full list fills the context with contacts the model has to read one by one.

2. Which fields does the post recommend returning to an agent?

   A. name, image_url and file_type
   B. uuid, 256px_image_url and mime_type
   C. Every field the API returns

   Answer: A. Readable fields tell the model what to do next, while low-level identifiers mostly cost tokens.

3. What soft limit does OpenAI's guide suggest for functions available at the start of a turn?

   A. Fewer than 5
   B. Exactly 10
   C. At least 50
   D. Fewer than 20

   Answer: D. OpenAI suggests fewer than 20 and calls it a soft suggestion. For large tool sets, its guide offers tool search.

4. How did Anthropic make sure its tool improvements did not fit only its own evaluation tasks?

   A. It used only tasks Claude wrote
   B. It checked them on held-out test sets
   C. It cut the number of tasks

   Answer: B. Held-out tasks the tools were never tuned on show whether a gain carries over to new work.

## Frequently asked questions

### How do I write a good tool description for an AI agent?

Write it for a new hire who has never seen your system. Say what the tool does, when to use it and when not to, what each parameter means and its format, and what comes back. Then measure the change on real tasks.

### How many tools can an AI agent handle?

Fewer than most people load. Each extra tool costs tokens on every request and adds a chance of a wrong pick between similar tools. Keep the handful you use most loaded and put the rest behind tool search, which Claude and OpenAI's newer models both support.

### Should a tool return JSON, XML or Markdown?

Any of the three can work. Anthropic found the best format changes with the task and the agent, because models do better with formats that look like their training data. Run two formats on the same evaluation tasks and keep the better one.

### Are MCP tools different from function calling tools?

The model sees them the same way, as a name, a description and an input schema, so the design advice is the same. MCP is a standard way to serve tools from a separate server, explained in [What is MCP](https://mindket.com/blog/what-is-mcp), and it lets a server mark which tools make destructive changes.

### Can Claude improve my tools for me?

Yes. Give it the logs of a few failed runs, ask what confused the agent, and let it rewrite the descriptions and the code in one pass so the two stay consistent. Keep a change only if it holds up on tasks Claude never saw.

## Sources

- [Anthropic Engineering: Writing effective tools for agents, with agents](https://www.anthropic.com/engineering/writing-tools-for-agents) (read 2026-11-06)
- [Anthropic Engineering: Introducing advanced tool use](https://www.anthropic.com/engineering/advanced-tool-use) (read 2026-11-06)
- [OpenAI API docs: Function calling](https://developers.openai.com/api/docs/guides/function-calling) (read 2026-11-06)
- [Claude Cookbooks: Creating a customer service agent with client-side tools](https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/customer_service_agent.ipynb) (read 2026-11-06)
- [anthropic-sdk-python: ToolResultBlockParam (is_error)](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/types/tool_result_block_param.py) (read 2026-11-06)
- [Ryan Lopopolo on X: pass --silent to prettier](https://x.com/_lopopolo/status/2030357237731037528) (read 2026-11-06)
- [Ryan Lopopolo on X: MISTAKES.md, DESIRES.md and LEARNINGS.md](https://x.com/_lopopolo/status/2037307669464363433) (read 2026-11-06)
- [Anthropic on YouTube: Building with MCP and the Claude API](https://www.youtube.com/watch?v=aZLr962R6Ag) (read 2026-11-06)

[Browse MCP servers](https://mindket.com/mcp-servers)
