How-to › Expose your API to agents

How to name tools so an agent picks the right one from fifty#

Give every tool a noun and a verb, a description long enough to choose on, and a warning on anything destructive, then measure how often two tools still look alike.

Audience
Agent builder
Level
intermediate
Topic
Design tool schemas agents can use
Languages
TypeScript and JavaScript
Verified

An agent asked to find a meter calls the update tool instead. Your catalog has get, getById, getAll, search, searchMeters and meterSearch, each with a three-word description, and half of them are plausible answers to the same request. The agent is not confused, it is choosing between things that genuinely look alike.1

What you get

You will end up with a naming rule and descriptions long enough to choose on. Two measurements come with it: how many pairs of tools read alike, and how often a real question ties between them. This is for you if your catalog has grown past what one screen shows.

Short answer

Name every tool noun_verb, so the thing it acts on comes first and the catalog sorts into groups. Write a description a reader could choose on without seeing the others, and say in it when an operation cannot be undone. Then measure: count tools whose descriptions overlap, and count queries that tie between two tools.

You will need

Node 22 or later, and a tool catalog with more than a handful of entries. The description is the field a model reads, and the tool definition carries it alongside the name and the input schema. The guidance on writing tools for agents makes the same argument about descriptions from the model’s side.2

Voxgig maintains sdkgen. This page compares the naming its go-mcp target produces with operationId-derived names and with a catalog somebody curated by hand.

Approaches compared

ApproachWhen it fitsWhat it costs youWhen to pick something else
Hand-curated verb-first namesA small catalog somebody owns and revisitsVerbs collide first, so get and search appear across resourcesThe catalog is large enough that grouping matters
Noun-first names with a measurementAny catalog past a dozen tools, because the prefix does the groupingA convention to hold to, and a measurement to runThe catalog is five tools and nobody is confused
The sdkgen go-mcp namingYou generate the server and want entity-operation names everywhereNames decided by the model rather than by you, for better or worseYour tools do not map onto entities
openapi-mcp-generator operationId namesThe description already has good operation idsNames as good as the operation ids, which are often getMetersByIdThe description’s operation ids were never designed

Every one of these produces names, and only one of them tells you whether the names work. The measurement is the part worth adopting whichever naming you use, because a catalog is judged by how it behaves under real questions rather than by whether it follows a rule.

Noun first is worth the convention for a reason that has nothing to do with taste. A catalog sorted alphabetically groups by resource. A model reading the list sees every meter operation together, and a person scanning for one has a prefix to scan for.

Say what cannot be undone, in the description

The safety note goes where the model reads, not in your documentation.

if (/delete|retire|void|remove|purge/.test(tool.name) && !/cannot be undone/i.test(tool.description)) {
  findings.push(`${tool.name}: destructive and the description does not say so`)
}

A name that implies destruction is not enough. The description is the field that reaches the model with the most context. A sentence there is what an agent works with when it decides whether to ask its user first.

Keep descriptions to a shape. A verb, the object, the limits, and any warning, in that order, is enough structure that two tools rarely read alike by accident. It also makes a missing limit obvious to whoever reviews the catalog.

Measure the ties, not only the rules

The second half of the check is behavioral.

const scored = tools
  .map((t) => ({ name: t.name, score: words(`${t.name} ${t.description}`).filter((w) => terms.has(w)).length }))
  .sort((a, b) => b.score - a.score)
const top = scored[0].score
const tied = scored.filter((s) => s.score === top && top > 0)

Term overlap is cruder than what a model does, and that is fine here. A query that ties on a crude scorer is a query where two tools genuinely share their vocabulary, which is exactly what you want to find before an agent does.

Check it worked

Audit a careless catalog and a rewritten one with the same ten operations.

node demo.mjs
before: 10 tools, 22 findings
   1  confusable pair
  10  thin description
   1  undeclared destruction
  10  unqualified name
      get and getById: descriptions overlap 0.60
  2 tied  "find the meter with serial SN-40199" -> meterSearch, updateMeter
  1 tied  "retire the meter that was replaced" -> updateMeter
  0 tied  "show me the invoice line items" -> nothing matched
after: 10 tools, 1 findings
   1  confusable pair
      meter_retire and invoice_void: descriptions overlap 0.62
  1 tied  "find the meter with serial SN-40199" -> meter_update_serial
  2 tied  "retire the meter that was replaced" -> meter_read, meter_retire
  1 tied  "show me the invoice line items" -> invoice_read

Twenty-two findings became one, and the third query goes from matching nothing to matching invoice_read. Two results are worth being clear about rather than glossing over.

The remaining confusable pair is caused by the safety sentence itself: meter_retire and invoice_void both say the operation cannot be undone, so their descriptions overlap. That is a cost of the rule, and it is the right trade, because the two tools act on different nouns and no question ties between them.

The first query still lands on meter_update_serial rather than meter_search, because the word serial appears in both. Good names reduce ambiguity; they do not remove it, and the measurement is what tells you where it is left.

node --test names.test.mjs
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 111.86389

When it goes wrong

An agent picks a destructive tool for a read. The verb is buried and the description is short. Put the verb in the name, and make the description’s first clause the action.

Two tools are chosen at random between. Their descriptions differ only in the noun. Add what each one returns and what its limits are, which is usually the distinguishing detail. Two tools that still read alike after that are probably one tool with a parameter.

Renaming breaks every existing agent configuration. Tool names are an interface. Keep the old names as aliases for a window, and measure which are still called. A tool catalog is an interface with no versioning story, so the window is all you have.

The catalog is fine, but the agent still flounders. There are ninety tools and the useful ones are five. Ship fewer tools to that agent rather than better names for all of them. A catalog is a menu, and a menu with ninety items is read by nobody.

When not to do this

Do not rename tools without a deprecation window. An agent configuration pointing at a name you removed fails at the moment somebody needs it, and the failure looks like the agent broke.

Do not put the warning about a destructive operation only in the input schema. Some clients show a schema and most show the description, and a warning nobody renders may as well not exist.

Do not accept generated names without running the measurement. The sdkgen go-mcp target and every other generator name tools from the model they were given.3 A model with two words for one resource produces a catalog with two words for one resource.

Last verified

Verified 2026-09-14 against Node 22.22.2. Both output blocks are what the preceding command printed.

Footnotes

  1. The protocol has met this catalog. The MCP specification asks that a tool name be 1 to 128 characters, case-sensitive and unique within its server. Its example of a collision across servers is two of them each exposing a search tool. Its three example valid names are getUser, DATA_EXPORT_v2 and admin.tools.list, one in each of three casings, which is a specification declining to have an opinion. ↩︎ Back to text

  2. The post also takes a position on prefixes, up to a point. Its examples group tools by service and then by resource, asana_projects_search and asana_users_search, which puts the verb last. It reports that choosing between a prefix and a suffix had non-trivial effects on its evaluations, and that the effect varies by model. Which way it cuts is left to the reader’s own evaluations. ↩︎ Back to text

  3. getMetersById is what an operation id looks like when nobody designed it. The OpenAPI specification does not require the field, and where one is present asks only that it be unique across the document, case-sensitive, and follow common programming naming conventions. Which conventions is not said. Tools and libraries may use the id to identify the operation, the specification adds, and a generator that names tools from it has taken that permission at its word. ↩︎ Back to text

Read this page as markdown · All how-to guides

Generate the client instead of writing it#

Retries, timeouts, pagination and auth are the same problems in every client. Voxgig generates them from your OpenAPI description, in 23 languages, from one model.

Get the Voxgig dispatch

Short notes on building SDKs, CLIs, REPLs, and MCPs for API-first teams, plus the occasional Fireside episode pick.

By signing up you agree to our Terms and Conditions.