Skip to content
back to blog

The number every MCP server needs to know: 64

7 min read

#mcp #ai-platform #outage #production


In late April 2026, an MCP-backed workflow on our platform stopped responding for the better part of a day.

The chat would route a user message through the model, the model would emit a tool call, and the call would fail before the tool ran. By the time we had the incident bundle and the runtime fix in place, a single number had been burned into the platform: 64.

This is the story of that number: what the Model Context Protocol spec actually enforces about it (nothing; its tool-name rule is a SHOULD the SDKs don't check), what every major LLM provider had quietly agreed on (64), and the runtime pattern that now keeps MCP server names compatible with the entire ecosystem.

The symptom

Tool names in the MCP ecosystem follow the canonical mcp__<server>__<tool> shape, straight out of Claude Code's plugin convention. With descriptive server and tool segments, a real tool name can comfortably run past 80 characters: an mcp__<some_business_app_connector>__<some_descriptive_action> is almost guaranteed to overflow once the server team picks names a human can read.

The model emits a tools array containing one such name, the provider receives it, and the provider rejects the request:

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "tools.0.name: String should have at most 64 characters"
  }
}

Same error from AWS Bedrock. Same error from native Anthropic. The OpenAI API rejected with a slightly different shape but the same outcome: 64 characters, no more.

The MCP specification didn't enforce a tool-name length — the rule that now exists (SEP-986, more on that below) is a SHOULD, not a hard cap. The Anthropic SDK schema documented a length but did not enforce it client-side. The tools/list endpoint accepted whatever the server returned. The mismatch lived at the provider boundary, and the provider was unforgiving.

How three providers agreed without speaking

I ran the same payload against all three majors to confirm the cap was real and consistent. Every one of them rejected at > 64:

ProviderError
AWS Bedrock (Anthropic)ValidationException: tool name length must be less than or equal to 64
Native Anthropic Messagesinvalid_request_error: tools.0.name: String should have at most 64 characters
OpenAI Chat CompletionsBadRequestError: tools[0].function.name should be at most 64 characters

Three companies, three independent SDK teams, one shared decision: roughly ^[a-zA-Z0-9_-]{1,64}$ for tool identifiers. None of them documented the choice prominently in their getting-started guides; none of them deviated from it.

The MCP ecosystem had been bumping into the limit for months. The issue tracker on anthropics/claude-code alone carries several variants of the same bug:

  • #19882 — Tool search fails when MCP tool names exceed 64 chars
  • #20983 — MCP plugin tool names exceed 64-character API limit
  • #21050 — MCP tool name exceeds 64 character limit with UUID-based server IDs
  • #21136 — API Error 400: MCP tool names exceeding 64 character limit
  • #23149 — Plugin MCP tool names can exceed API's 64-character limit

From the other side of the contract, github/github-mcp-server opened issue #520 — 64-Character Limit on Tool Names Conflicts with MCP Spec — Should Be Removed or Configurable. Same number, different angle: the spec didn't enforce a length, and every implementation did. (Tool-name length is far from the only sharp edge in the MCP ecosystem — I pulled on another one in a stored XSS in an MCP registry.)

The shape of the fix

Two patterns proved enough to close the gap. I'll describe them in the abstract rather than copy-paste production code, but the shape is straightforward enough to implement from the description.

A runtime alias sanitiser. When the MCP client builds its tool registry, every name that exceeds 64 characters is rewritten to a stable, deterministic alias that fits. The two non-obvious bits:

  • The shortened name must preserve the mcp__<server>__<tool> shape so existing routing logic keeps working. Hashing the server segment to a short, stable prefix (a 12-char digest of a cryptographic hash works fine) and keeping the tool segment human-readable hits both goals — the alias survives a router restart, identical servers map to identical aliases, and the tool stays recognisable in traces and logs.
  • The mapping must be bidirectional. The model emits an alias on turn N, the provider returns it on the tool-call response, and the router needs to translate it back to the underlying MCP tool to actually invoke anything.

A pre-flight validator at the server-registration boundary. Servers that publish tool names with no plausible 64-char shortening — names that lack the mcp__server__tool structure, or names so long the shortened form still overflows — are refused at registration time. Failing fast at the registration boundary is cheaper than triaging an outage at the provider boundary.

The combined effect is that any MCP server can publish whatever name it likes, the router quietly normalises it to a provider-safe form, and the model never sees a name it can't actually call.

What SEP-986 changes

SEP-986 — Specify Format for Tool Names had been open against the MCP specification since July 2025, and has since reached Final status. It specifies that tool names should be 1–64 characters, case-sensitive, drawn from alphanumerics plus _, -, ., and /. The 64 matches what every major provider was already enforcing in private; the character set is a little more permissive: it allows / for namespacing (e.g. user-profile/update), which the providers' stricter ^[a-zA-Z0-9_-]{1,64}$ does not.

Two things are worth pulling out. First, the spec's "1–64" is a SHOULD, not an enforced cap — so the gap that bit us (a SHOULD the SDK doesn't check, a hard limit the provider does) survives the spec. Second, SEP-986's own backwards-compatibility guidance is to support non-conforming names as aliases with a deprecation path. That is the alias-sanitiser pattern above, almost to the letter — the spec landed on the same fix.

What this teaches

Three patterns that compound across the MCP ecosystem:

An unenforced spec leaves implementers free to converge. They will converge on the most restrictive option, because that's the one that protects everyone downstream. The MCP spec didn't enforce 64 (even now it's only a SHOULD), so the SDK teams at Anthropic, AWS, and OpenAI each enforced 64 themselves, because permitting longer names meant either dropping support somewhere or paying for it at deserialisation. The cautious answer is usually the right answer.

Provider boundaries are spec boundaries. Anything that ships through the LLM API contract — tool names, JSON schemas, system prompts, message ordering — is constrained by the strictest provider you target. If you support Bedrock, Anthropic and OpenAI, you live in the intersection of three private specs that no document captures.

Aliasing is healthier than truncation. Truncating an 80-character tool name to its first 64 characters loses information and collides under load — two distinct tools that happen to share a long common prefix both end up at the same alias. A deterministic alias derived from a stable hash preserves the call-graph integrity, and an alias-to-real map keeps the human-readable form available where it's helpful — logging, traces, tool catalogues.

Production AI platforms run on more constraints than the documentation admits. Living in the intersection is a job in itself.

References