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
| Approach | When it fits | What it costs you | When to pick something else |
|---|---|---|---|
| Hand-curated verb-first names | A small catalog somebody owns and revisits | Verbs collide first, so get and search appear across resources | The catalog is large enough that grouping matters |
| Noun-first names with a measurement | Any catalog past a dozen tools, because the prefix does the grouping | A convention to hold to, and a measurement to run | The catalog is five tools and nobody is confused |
| The sdkgen go-mcp naming | You generate the server and want entity-operation names everywhere | Names decided by the model rather than by you, for better or worse | Your tools do not map onto entities |
| openapi-mcp-generator operationId names | The description already has good operation ids | Names as good as the operation ids, which are often getMetersById | The 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.
Related how-tos
Last verified
Verified 2026-09-14 against Node 22.22.2. Both output blocks are what the preceding command printed.
Footnotes
-
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_v2andadmin.tools.list, one in each of three casings, which is a specification declining to have an opinion. ↩︎ Back to text -
The post also takes a position on prefixes, up to a point. Its examples group tools by service and then by resource,
asana_projects_searchandasana_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 -
getMetersByIdis 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