A missing description makes your agent hesitate. A near-duplicate tool name makes it confidently call the wrong tool — and that's the nastier failure mode.
That line isn't mine. It came from arobakid, who commented on my original mcp-lint article last week. He works at Elva, where he scores OpenAPI-to-MCP name conversions all day — this is his production pain, not a thought experiment. He asked for a near-duplicate tool-name detection rule, then volunteered to test it. So v0.2 is basically his rule.
What it catches
mcp-lint now checks every pair of tools in tools/list and flags confusable pairs:
-
Word-order swaps —
search_itemsvsitem_search(plural-insensitive, so a singular/plural flip doesn't hide the swap) -
Identical names modulo separators and case —
backup_dbvsbackup-db -
Levenshtein-close names (≤ 2 edits, 4+ chars) with corroborating evidence: overlapping input shape, or near-identical descriptions.
get_uservsget_userssharing the same params gets flagged;addvsanddoes not.
The guard rails matter: edit distance alone would drown you in false positives, so the rule demands a second signal before it fires.
mcp-lint audit — 2 tool(s), design score: 88/100
search_items (score 88/100)
- [near-duplicate-names] near-duplicate of 'item_search' (same words, different order/plural) (-12)
Try it
Still stdlib-only, still one file:
git clone https://github.com/hahahahahahahahah6/mcp-lint
cd mcp-lint
python3 mcp_lint.py audit tools.json
tools.json is a bare array of tool definitions or a tools/list JSON-RPC result. 23/23 smoke tests green, including 9 for the new rule.
One honest note: there is no PyPI package for this one — the mcp-lint name on PyPI belongs to an unrelated project, so install from GitHub.
Thanks again to arobakid for the rule and for signing up as its first tester. If you maintain an MCP server with more than a handful of tools, run the audit — you might find your agent has been choosing between two almost-identically-named tools this whole time.
Top comments (1)
Nice rule, and asking for a second signal before it fires is the right call. One case that might be worth covering: names that clash across servers. Each server can pass on its own, but once a client loads two of them, "search" from one and "search_items" from the other sit side by side in the same tool list. Letting one audit run take several tools.json files would catch that. Also, from my own server: a directory scorer marked it down until every parameter had a description, not just every tool. A check for empty parameter descriptions would go well with the name rule.