Quick answer: An MCP server can expose every operation correctly and still fail in practice, because the model chooses a tool by reading its name and description. When descriptions are generic, duplicated, or copied from method names, the agent cannot distinguish near-similar operations and calls the wrong one — not for lack of capability, but for lack of signal. Writing precise, distinction-focused descriptions is part of the tool contract, not documentation polish, and it is the cheapest lever you have for reliable tool selection.

This failure is easy to misdiagnose as a model limitation. The tools are there, the schema is valid, and the agent has permission to call them. Yet it reaches for createInvoice when the user asked to update one, or picks a list endpoint and passes an ID that belongs to a different resource. The root cause is that the selection decision is made from text the server provides. If two tools read similarly, the model's choice between them is effectively a guess. Improving the model without fixing the descriptions just makes a confident guess.

What vague descriptions look like

Auto-generated MCP servers often inherit descriptions from OpenAPI summaries, which are written for humans reading a reference page, not for a model choosing an action. Three anti-patterns dominate.

  • The method-name echo. getInvoice described as "Get invoice" adds no information beyond the name and says nothing about when to use it.
  • The duplicated description. Two operations such as list and search carry the same sentence, so the model has no basis to pick the correct one.
  • The missing boundary. Create versus update, or draft versus publish, are described without the condition that distinguishes them, leaving the agent to infer from the user's wording.
// what the model has to choose from (weak)
{
  name: "list_invoices",
  description: "List invoices"
},
{
  name: "search_invoices",
  description: "Search invoices"
}
// Both look like "find invoices"; the agent picks arbitrarily.

Descriptions that carry the decision

A strong description answers three questions in one or two sentences: what the tool does, when to use it, and when not to use it. Leading with the user intent and stating the boundary makes the distinction explicit so the model does not have to infer it.

{
  name: "list_invoices",
  description: "List the most recent invoices for the authenticated account, paginated. Use for browsing or showing the current set; do not use for lookups by customer or status."
},
{
  name: "search_invoices",
  description: "Search invoices by customer, status, or date range. Use when the request includes a filter or a specific customer; for a simple list of recent invoices use list_invoices."
},
{
  name: "create_invoice",
  description: "Create a new draft invoice. Use only when no existing invoice matches; to change an existing invoice use update_invoice."
}

Each description now points away from its neighbor as well as toward itself. The "do not use" clause is the highest-value part: it hands the model the negative condition that prevents the wrong call.

Weak versus decision-grade descriptions

Tool pairVague descriptionDistinction to add
list vs search"List invoices" / "Search invoices"Browse recent vs filter by a field
create vs update"Create invoice" / "Update invoice"New record vs existing ID present
draft vs publish"Draft invoice" / "Publish invoice"Not yet issued vs finalize and send
get vs list"Get invoice" / "Get invoices"One known ID vs a collection
cancel vs refund"Cancel" / "Refund"Stop pending vs return captured money

Keep descriptions tied to the spec

OpenAPI already contains operation summaries, descriptions, and the request schema that define what each operation does. When an MCP server is generated from the spec, enriching the operation descriptions there means the tool text stays in sync with the actual behavior and parameters rather than being maintained separately in the MCP layer. A generator that surfaces the intent, parameters, and the "use when" boundary from the contract gives the model the signal it needs without a second source of truth. This also prevents an agent editing the MCP server by hand from drifting descriptions away from what the API really does.

A practical review loop

  1. List every tool and read its description without the name; if you cannot tell when to use it, the model cannot either.
  2. For each near-similar pair, write the boundary condition that separates them.
  3. Run the same user request against the server and confirm the correct tool is selected; repeat for the ambiguous wording that previously failed.
  4. Fix the description in the OpenAPI source so the change regenerates cleanly.

Frequently asked questions

Is this really a server problem and not a model problem?

It is a server-provided signal problem. The model selects from the text you expose; generic text makes selection a guess. Testing the same request with improved descriptions usually resolves the wrong-tool error without changing the model, which is the evidence that the description was the gap.

How long should a tool description be?

One to two sentences is usually enough. The goal is not length but the decision boundary: what it does, when to use it, and when to prefer a neighbor. Padding with redundant detail adds noise and can dilute the signal.

Should I add examples to the description?

A short trigger phrase can help, but the parameter schema should carry structured examples. Reserve the description for the use boundary; keep payload examples in the input schema so they stay valid as the API changes.

What if many operations are genuinely similar?

Find the real condition that separates them — the presence of an ID, a filter, or a state change — and state it. If two operations are truly indistinguishable in intent, they may be candidates to consolidate rather than to expose as separate tools.

This is part of the MCP Server Guide — start there for the full design of tools an agent can reliably select and call.