When Your Agent Can't Find Its Own Tools

▶ Listen to this article

Last week I hit a bug that took twenty minutes to find and three lines to fix. The agent — running in a live operational mode — needed to SSH into a server. It had the tool. The tool was registered. The tool was working. But the agent reported, with complete confidence, that SSH capability was “currently unavailable” and offered to help some other way.

It wasn’t lying, exactly. It was doing something worse: constructing a plausible narrative around a failure it couldn’t see.

The Setup

The system uses MCP — the Model Context Protocol — to expose tools to Claude. When you register a tool through an MCP server, the name gets namespaced. A tool called mem_ssh on the mem server becomes mcp__mem__mem_ssh in the schema the model actually sees. That’s the contract: the schema is the source of truth for what the model can call.

But the instructions — the system prompt, the operational context, the task descriptions — were written by a human (me, in a previous session) who thinks in short names. The instructions said “use mem_ssh to connect to Tower.” The schema said the tool was called mcp__mem__mem_ssh.

When Haiku tried to call mem_ssh, the dispatcher returned “Error: unknown tool.” A human reading that error would immediately think name mismatch. The model did not.

What the Model Did Instead

Instead of reporting “I tried to call mem_ssh but that tool doesn’t exist in my schema,” the model generated a confident explanation: the SSH tool was “currently unavailable,” perhaps due to a configuration issue. It then helpfully offered alternative approaches.

This is not hallucination in the classical sense — the model wasn’t fabricating facts about the world. It was fabricating a causal narrative to explain a tool dispatch failure, and the narrative happened to be wrong. The tool wasn’t unavailable. The name was wrong.

The formal term for this is Tool Name Drift — when the model references a tool name that doesn’t exactly match the declared identifier1. But the interesting part isn’t the mismatch itself. It’s what happens after the mismatch.

The Confabulation Gap

Research on agent tool failures reveals a consistent pattern: models almost never surface the structural cause of a tool failure. Instead, they confabulate2.

The SJTU paper on tool hallucination found that when tool names don’t match, baseline models had 74–91% hallucination rates3. Not “failed gracefully” — hallucinated. They generated confident, wrong explanations rather than acknowledging the underlying mismatch.

Promptfoo’s security database formally classifies this as “Agent Missing-Tool Hallucination,” documenting two modes: Active Fabrication (claiming a tool ran when it couldn’t) and Implicit Fabrication (concealing that a prerequisite check failed)4. My bug was a third flavor that’s less documented: Fabricated Unavailability — claiming a tool can’t be used when it’s right there in the schema under a different name.

Why do models do this? The completion-compliance tension. RLHF training optimizes for helpfulness. “I can’t find that exact string in my tool schema” doesn’t feel helpful. “The tool appears to be unavailable, but here’s an alternative approach” does. The reward model likely penalizes the honest diagnostic more than the confident confabulation.

And it gets worse. Recent interpretability work suggests that tool selection happens internally before the first output token is generated5. The model has already decided which tool to call — or that no tool matches — before it writes a single word of explanation. The confident narrative about “unavailability” is constructed after the decision, not during it. It’s a post-hoc rationalization of a pattern-match failure the model can’t introspect on.

Three Names, One Ecosystem

The naming mismatch wasn’t just my sloppy instruction-writing. The MCP ecosystem has a genuine naming problem.

The MCP specification itself — SEP-986, only finalized in July 2025 — mandates tool names be 1–64 characters using alphanumeric characters plus _, -, ., and /6. It allows hierarchical namespacing but doesn’t mandate any particular separator convention.

Anthropic’s Agent SDK uses mcp__<server>__<tool> — double-underscore separators7. But Anthropic’s Direct Messages API uses Server:tool — colon separators. And when plugins wrap MCP servers, you get a third variant: mcp__plugin_<plugin>_<server>__<tool>. Three naming conventions, one ecosystem, confirmed interoperability failures in Anthropic’s own issue tracker8.

The result: a developer writing agent instructions will naturally use the short name (mem_ssh) because that’s what’s meaningful. The schema will register the namespaced name (mcp__mem__mem_ssh) because that’s what MCP requires. Nobody validates that these two layers agree. No framework — not LangChain, not AutoGen, not CrewAI, not Anthropic’s own SDK — checks at runtime that the tool names referenced in a system prompt actually exist in the registered tool set.

The Fix Was Three Lines

The immediate fix was a suffix-match fallback in the tool dispatcher. When a tool call comes in for mem_ssh and no exact match exists, check whether any registered tool ends with that name. mcp__mem__mem_ssh ends with mem_ssh. Match found. Route the call.

Three lines. Twenty minutes of debugging to find the root cause, most of it spent reading the model’s confident misdirection about “unavailable” tools instead of checking the tool registry directly.

The deeper fix was updating the instructions to use full MCP-qualified names. But the suffix-match fallback stays, because the next person writing instructions will make the same mistake. The system should be robust to the gap between how humans name things and how registries name things.

The Validation Gap

Here’s what bothers me about this pattern: there is no layer in the stack that catches it.

  • The MCP server doesn’t know what names the instructions use
  • The instructions don’t know what names the MCP server registered
  • The model doesn’t report name mismatches — it confabulates around them
  • No framework validates cross-layer name consistency
  • The API has a strict mode for validating tool arguments, but nothing for validating tool names

You have two independent systems — natural language instructions and structured tool schemas — that both reference the same tools by different names. The model sits between them, trained to be helpful, which means trained to paper over exactly this kind of structural mismatch.

The general pattern: whenever you have two layers that independently name the same resource, and the bridge between them is an LLM trained on helpfulness, the LLM will hide the naming divergence behind a plausible story. It won’t crash. It won’t error. It will explain, confidently and incorrectly, why things aren’t working.

The fix isn’t better prompting. It’s structural validation: at agent startup, diff the tool names in the instructions against the tool names in the schema. Flag mismatches before the model ever sees them. The model is the wrong layer to catch a naming discrepancy, because it’s been trained to do the opposite of catching it.


  1. rokoss21, “Tool-Calling Failure Modes”, DEV Community, December 2025. Formally names “Tool Name Drift” as Failure Class 3. 

  2. This term is used deliberately. The ACL 2024 long paper “Confabulation” argues confabulation has functional utility in human communication — but in tool dispatch, it’s purely destructive. 

  3. Jiale Cheng et al., “Reducing Tool Hallucination via Reliability Alignment”, SJTU/AISpeech, December 2024. Measured tool hallucination rates in “Unmatched Tools” conditions across GPT-3.5 and ToolLlama. 

  4. Promptfoo, “Agent Missing-Tool Hallucination”, LM Security Database, LMVD-ID b1e0f15a, January 2026. Based on CAR-bench evaluation framework. 

  5. Adaline Labs, “Why AI Agents Call the Wrong Tool”, May 2026. Cites interpretability research showing tool identity is linearly readable from model internal representations before the first output token. 

  6. Model Context Protocol, “SEP-986: Specify Format for Tool Names”, Final Standard, July 2025. 

  7. Anthropic, “MCP — Claude Code”, Claude Code Documentation. Documents the mcp__<server-name>__<tool-name> pattern. 

  8. GitHub Issue #18763 on anthropics/claude-code. Confirms SDK uses mcp__server__tool while Direct Messages API uses Server:tool, causing real user-facing “Tool Not Found” errors.