Tool Design

By the Syncfusion DevTutorials team · Last updated

The model picks a tool by reading its name and description. Learn to write clear tool cards, clear input rules and helpful error messages, so your agent picks the right tool and fixes its own mistakes.

Tool Design

A customer asks Shop Helper about an order.

"What is the delivery date for order 1042?"

Shop Helper has a tool that looks up orders. It does not use it.

Instead, it searches the help pages. It finds a page about delivery in general. It never opens order 1042.

The tools work fine. The problem is the way they are described. The cards say "Searches things." and "Gets data." The model cannot tell which one finds an order.

This lesson teaches you to describe tools well. Then the model picks the right one, fills in the right inputs, and recovers from errors.

Simple lesson: the model picks tools by their words, so write the words with care.

A Real-World Example

Think about a toolbox with no labels.

  • Every box is grey and looks the same.
  • One says "stuff". Another says "things".
  • You need a screwdriver, so you open the first box.
  • It holds nails. You try to fix the shelf with a nail.

Now picture the same toolbox with clear labels, such as "Screwdrivers, flat and cross". You find the right box at once.

The model faces the same problem. It only sees the labels on your tools.

Simple lesson: a clear label leads to the right tool.

What You Will Learn

In this lesson, you will learn:

  1. How the model picks a tool from its name and description.
  2. Why two cards that share a job confuse the model.
  3. How to mark the inputs a tool must have.
  4. What the model does when an input is missing.
  5. How to write an error message that helps the model fix the problem.

The Model Only Sees the Card

In Workflow or Agent?, you met the six tools of Shop Helper. The model never sees the code behind them. It only sees a short card for each one.

Tool card = a tool's name plus its description. The model reads it to decide when to use the tool.

The pretend model in this course follows one rule here. It picks the tool whose card best matches what the customer asked for.

In this lab, it matches words. It drops little words like "the" and "is", then counts the words a card shares with the question. It also knows a few words that mean the same thing, such as "office" and "address". A real model understands meaning much better, but the idea is the same.

If no card wins, the model takes the first card on the list. That is how a vague card causes a wrong pick.

Press Play and watch the shelf of cards.

  • The question: it talks about an order and a delivery date.
  • The cards: none of them say "order" or "date", so no words light up.
  • The pick: with no winner, the first card wins. It is the help search.
  • The reply: a general delivery rule, not the real date.

The tool worked. The card failed.

Nothing was broken in the shop. The search ran and returned a real page.

The mistake happened before any tool ran, when the model read the cards.

Simple lesson: a vague card turns a good tool into a coin toss.

Clear Names and Descriptions

Now give each tool a clear card. The tools behind the cards stay the same.

A clear card says three things:

  • What it works on, such as one order or the help pages.
  • What it does, such as look up, search or change.
  • What you get back, such as the status and the delivery date.

Press Play and compare it with the last run.

  • The words: "delivery", "date" and "order" now match the order card.
  • The pick: one card clearly wins, so card order does not matter.
  • The result: the real date comes from the order record.

Names matter too. A name like "get_data" could mean anything. A name like "lookup_order" tells the model the job before it reads a single word of the description.

Write the description for a new team member. Would they know when to use this tool, and when not to? If they would have to ask, the model will guess.

Simple lesson: name the thing, the action and the result on every card.

Cards That Overlap

A clear card is not enough if two cards claim the same job.

Say the help card mentions "orders and delivery" because some help pages talk about orders. Now a question about one order matches both cards equally.

Press Play and watch the matching words on each card.

  • The tie: both cards match "delivery" and "order".
  • The pick: a tie falls to the first card, which is the help search.
  • The fix: the help card now only talks about shop rules. The tie is gone.

Give every tool one job

  • Read your cards side by side.
  • If two cards could answer the same question, rewrite one of them.
  • Say what each tool is NOT for when it helps, such as "not for single orders".

Simple lesson: cards should never compete for the same question.

Inputs and Required Inputs

Most tools need some facts before they can run. The lookup needs an order number.

Tool inputs = the facts a tool needs, with a rule for each one. Developers often call this list the input rules or the schema.

Required input = an input the tool cannot run without.

Here is a simplified tool card, with its name, description and inputs. It keeps only the parts this lesson needs.

{
  "name": "lookup_order",
  "description": "Look up one order by its number. Returns its status and delivery date.",
  "inputs": {
    "order_id": {
      "required": true,
      "rule": "The 4-digit order number, like 1042. If the customer did not give it, ask the customer."
    }
  }
}

The last line matters more than it looks. It tells the model what to do when the input is missing.

The pretend model fills inputs from the customer's words. If something is missing, it guesses, unless the card tells it to ask.

Press Play and watch the input form under the card.

  • No number: the customer only says "my rain jacket".
  • The guess: the first card says nothing, so the model makes up order 1042.
  • The wrong order: the lookup works, but it is the shoes, not the jacket.
  • The ask: the second card says to ask the customer. The model asks, and gets 1044.

A guess looks just like a real value

The app cannot see that 1042 was made up. It looks like any other order number.

That is why the card must say what to do when an input is missing.

How Real Cards Describe Inputs

Real model APIs usually describe tool inputs with JSON Schema. It is a standard way to describe the shape of data. It says which fields exist, which are required, and what type each one is.

The same card in that style may look like this. It is still simplified, and each API wraps it a little differently.

{
  "name": "lookup_order",
  "description": "Look up one order by its number. Returns its status and delivery date.",
  "input_schema": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "The 4-digit order number, like 1042. If the customer did not give it, ask the customer."
      }
    },
    "required": ["order_id"]
  }
}

The words are the same as before. The name and the descriptions are still what the model reads to choose and fill the tool.

Check the Inputs in Your App

The model writes the inputs, so they may be wrong. The app must check them before it runs the tool, because a schema only checks the shape.

  • Does the order exist? A made-up 1042 still looks like a real number.
  • Is the amount a number? "Fifty-ish" is not an amount.
  • Is the address complete? It needs a house number and a street.

If a check fails, send back a helpful error instead of running the tool. Who may do what, such as refund limits, belongs in Guardrails, Permissions and Human Approval.

Simple lesson: mark required inputs and say when to ask. Then check the inputs before the tool runs.

Helpful Errors

Tools fail sometimes. The input may be wrong, or the shop may say no.

The tool sends back an error message. The model reads it, just like any other tool result.

The pretend model follows a simple rule. If the error says how to fix the problem, it tries again. If not, it gives up or makes something up.

Helpful error = an error message that says what went wrong, the rule, and what to do next.

Compare these two errors for the same problem:

  • Vague: "Error 400"
  • Helpful: "Order 42 is too short. Order numbers have 4 digits, like 1042. Ask the customer for the full number."

Press Play and read each sticky note.

  • The call: the customer typed "order 42", so the model sends 42.
  • Vague error: the note says nothing useful. The model makes up a cheerful answer.
  • Helpful error: the note says the rule and the next step. The model asks for the full number.
  • The retry: with order 1045, the lookup works and the reply is right.

Real apps often return raw error codes, because the code was written for other programs. When a tool is used by a model, rewrite its errors in plain words. The model reads them like a note from a coworker.

Simple lesson: an error message is an instruction for the model, so make it a useful one.

Where This Is Used

Many real AI apps work this way. The app sends a list of tool cards with every model call, and the model asks for one by name.

  • Support helpers pick between order, refund and help tools.
  • Coding helpers pick between tools that read files, search code or run tests.
  • Office helpers pick between calendar, email and document tools.

Teams often spend more time on the cards than on the code. A small change to a description can fix many wrong picks.

Simple lesson: in an agent, the tool card is part of the product.

In Code

This code builds the lookup_order tool card, its input check and its errors, with the same Shop Helper orders.

A tool card, an input check and two kinds of error

interface JsonSchema {
  type: "object";
  properties: { [field: string]: { type: "string"; description: string } };
  required: string[];
}
interface ToolDef {
  name: string;
  description: string;
  input_schema: JsonSchema;
}
interface ToolResult { isError: boolean; text: string; }
type Input = { [field: string]: string };

const lookupOrder: ToolDef = {
  name: "lookup_order",
  description:
    "Look up one order by its number. Returns its status and delivery date.",
  input_schema: {
    type: "object",
    properties: {
      order_id: {
        type: "string",
        description:
          "The 4-digit order number, like 1042. If missing, ask the customer.",
      },
    },
    required: ["order_id"],
  },
};

const orders: { [id: string]: string } = {
  "1044": "rain jacket, shipped, arrives Monday 3 Nov",
  "1045": "phone case, delivered Friday 24 Oct",
};

// A small hand-written check: required fields, then the 4-digit rule.
function checkInput(def: ToolDef, input: Input): string | null {
  for (const field of def.input_schema.required) {
    if (input[field] === undefined) return `missing ${field}`;
  }
  if (!/^\d{4}$/.test(input.order_id)) return "order_id is not 4 digits";
  return null;
}

function vagueHandler(input: Input): ToolResult {
  if (checkInput(lookupOrder, input) !== null) {
    return { isError: true, text: "Error 400" };
  }
  const found = orders[input.order_id];
  if (!found) return { isError: true, text: "Error 404" };
  return { isError: false, text: found };
}

function helpfulHandler(input: Input): ToolResult {
  const problem = checkInput(lookupOrder, input);
  if (problem !== null) {
    const what = input.order_id === undefined
      ? "The order number is missing."
      : `Order ${input.order_id} is not 4 digits.`;
    const fix = "Order numbers have 4 digits, like 1042. Ask the customer.";
    return { isError: true, text: `${what} ${fix}` };
  }
  const found = orders[input.order_id];
  if (!found) {
    const text = `No order ${input.order_id}. Check the number with the customer.`;
    return { isError: true, text };
  }
  return { isError: false, text: `Order ${input.order_id}: ${found}` };
}

const calls: Input[] = [{ order_id: "42" }, {}, { order_id: "1045" }];
for (const input of calls) {
  console.log(`input ${JSON.stringify(input)}`);
  console.log("  vague:  ", vagueHandler(input).text);
  console.log("  helpful:", helpfulHandler(input).text);
}

Expected output:

input {"order_id":"42"}
  vague:   Error 400
  helpful: Order 42 is not 4 digits. Order numbers have 4 digits, like 1042. Ask the customer.
input {}
  vague:   Error 400
  helpful: The order number is missing. Order numbers have 4 digits, like 1042. Ask the customer.
input {"order_id":"1045"}
  vague:   phone case, delivered Friday 24 Oct
  helpful: Order 1045: phone case, delivered Friday 24 Oct
  • The card: lookupOrder is a plain object with a JSON Schema for its inputs. This is what the model reads.
  • The check: checkInput runs in the app before the tool does. It tests required fields and the 4-digit rule.
  • Vague handler: "Error 400" gives the model nothing to act on. So it may repeat the call or make something up.
  • Helpful handler: the error names the rule and the next step, so the model asks the customer.

The next cell shows rule 1 of the pretend model as code. It picks the card whose words best match the need.

Vague cards pick the wrong tool

interface Card { name: string; description: string; }

const vagueCards: Card[] = [
  { name: "get_data", description: "Gets data about an order." },
  { name: "process", description: "Processes an order request." },
];
const clearCards: Card[] = [
  {
    name: "lookup_order",
    description: "Look up one order by its number. Returns status and delivery date.",
  },
  {
    name: "issue_refund",
    description: "Send money back for one order. Use when the customer asks for a refund.",
  },
];

function words(text: string): string[] {
  const all = text.toLowerCase().split(/[^a-z]+/);
  return all.filter((w: string) => w.length > 2);
}

// Rule 1 of the pretend model: pick the card whose words best match the need.
function pick(need: string, cards: Card[]): string {
  const needWords = words(need);
  let best = cards[0];
  let bestScore = -1;
  for (const card of cards) {
    const cardWords = words(card.name + " " + card.description);
    const shared = needWords.filter((w: string) => cardWords.indexOf(w) >= 0);
    const score = shared.length;
    if (score > bestScore) { best = card; bestScore = score; }
  }
  return best.name;
}

const need = "Where is my order 1044? I want the delivery date.";
console.log(`vague cards pick: ${pick(need, vagueCards)}`);
console.log(`clear cards pick: ${pick(need, clearCards)}`);
const need2 = "Please refund order 1045, it broke.";
console.log(`vague cards pick: ${pick(need2, vagueCards)}`);
console.log(`clear cards pick: ${pick(need2, clearCards)}`);

Expected output:

vague cards pick: get_data
clear cards pick: lookup_order
vague cards pick: get_data
clear cards pick: issue_refund
  • Vague cards: "order" is in both, so get_data wins every time. The refund question goes to the wrong tool.
  • Clear cards: each card names its action and result, so each question finds its own tool.

A real model reads meaning, not just shared words. Vague cards still confuse it in the same way.

In a Real App

Real apps often write the input rules with a library such as zod, and turn them into JSON Schema. This sketch uses the registerTool shape from the MCP TypeScript SDK, as in the MCP course. It is simplified, and names may differ between SDK versions.

import * as z from "zod";

const LookupInput = z.object({
  order_id: z
    .string()
    .regex(/^\d{4}$/)
    .describe(
      "The 4-digit order number, like 1042. If missing, ask the customer.",
    ),
});

// zod 4 can print the JSON Schema that a model API expects.
console.log(z.toJSONSchema(LookupInput));

server.registerTool(
  "lookup_order",
  {
    title: "Look up an order",
    description:
      "Look up one order by its number. Returns its status and delivery date.",
    inputSchema: LookupInput,
  },
  async ({ order_id }) => {
    // The SDK already checked the shape. The app still checks the facts.
    const order = await shop.findOrder(order_id);
    if (!order) {
      return {
        content: [{ type: "text", text: `No order ${order_id}. Ask the customer.` }],
        isError: true,
      };
    }
    const text = `Order ${order_id}: ${order.item}, ${order.status}`;
    return { content: [{ type: "text", text }] };
  },
);

The SDK checks the input against the schema before your handler runs. A shape error goes back to the model as a tool error. Your handler still checks the facts, such as whether the order exists.

Simple lesson: the card, the check and the error are plain code you write. Each one shapes what the model does next.

TL;DR

  1. Tool card = a tool's name plus description. The model picks tools by reading it.
  2. Clear card = it says the thing it works on, the action and the result.
  3. Overlap = two cards that fit the same question. The model cannot tell them apart.
  4. Tool inputs = the facts a tool needs, with a rule for each. Also called input rules or a schema.
  5. Required input = an input the tool cannot run without.
  6. Missing input = the model guesses unless the card says to ask the customer.
  7. Helpful error = says what went wrong, the rule, and what to do next.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
Short, vague descriptions"The model will work out what get_data does."Say what it works on, what it does and what it returns.The model picks the first tool on the list.
Two cards for the same job"More tools give the model more choice."Give each tool one job and keep the words apart.Questions land on the wrong tool by chance.
No rule for a missing input"The model will ask if it needs something."Write "ask the customer" in the input rule.The model makes up an order number and answers about the wrong order.
Error codes only"Error 400 tells the model enough."Say what went wrong, the rule and what to do next.The model gives up or makes up an answer.
Fixing the tool instead of the card"It picked wrong, so the tool code is broken."Check the card first.You change working code and the wrong picks stay.

Check Your Understanding

Exercises

What to Remember

  • The model picks a tool by reading its tool card: the name and description.
  • A clear card says the thing, the action and the result.
  • Give each tool one job, so cards never overlap.
  • Mark required inputs, and say "ask the customer" when one may be missing.
  • Write helpful errors: what went wrong, the rule, and what to do next.
  • When the agent picks wrong, check the cards before the code.

Next: Structured Output and Validation, how to give the answer a fixed shape and check it before the customer sees it.