shareAI-lab/learn-claude-code · error · ValueError
Every MCP tool needs a non-empty name
Error message
Every MCP tool needs a non-empty name
What it means
MCPClient.register() sanity-checks the tool list before accepting it: every tool_def must carry a 'name' that is a non-empty string. This catches malformed registrations (in-process test servers, proxies, or hand-built tool lists) before any tool is exposed, because a nameless tool could never be addressed by call_tool.
Source
Thrown at s15_integrated_harness/code.py:2432
# -- MCP System --
# MCP is modeled as late-bound tools: connect first, then discovered server
# tools are merged into the normal tool pool with mcp__server__tool names.
class MCPClient:
"""Small in-process stand-in for MCP tools/list and tools/call."""
def __init__(self, name: str):
self.name = name
self.tools: list[dict] = []
self._handlers: dict[str, callable] = {}
def register(self, tool_defs: list[dict],
handlers: dict[str, callable]):
names = [tool.get("name") for tool in tool_defs]
if any(not isinstance(name, str) or not name for name in names):
raise ValueError("Every MCP tool needs a non-empty name")
if len(set(names)) != len(names):
raise ValueError(f"Duplicate MCP tool name on server {self.name!r}")
missing = [name for name in names if name not in handlers]
if missing:
raise ValueError(f"Missing MCP handlers: {', '.join(missing)}")
self.tools = list(tool_defs)
self._handlers = dict(handlers)
def call_tool(self, tool_name: str, args: dict) -> str:
handler = self._handlers.get(tool_name)
if not handler:
return f"MCP error: unknown tool '{tool_name}'"
try:
return str(handler(**args))
except Exception as exc:
return f"MCP error: {type(exc).__name__}: {exc}"
View on GitHub (pinned to 985456f4ad)
Solutions
- Ensure every tool definition has "name": <non-empty string>.
- Filter or fix malformed entries before register(): keep only dicts with valid names.
- If proxying a real server, pass through tools/list entries unmodified.
Example fix
// before
client.register([{"description": "echo"}], {"echo": echo_fn})
// after
client.register([{"name": "echo", "description": "echo"}], {"echo": echo_fn}) Defensive patterns
Strategy: validation
Validate before calling
def tools_well_named(tool_defs) -> bool:
return all(isinstance(t.get("name"), str) and t["name"] for t in tool_defs)
tool_defs = [t for t in tool_defs if isinstance(t.get("name"), str) and t["name"]] Type guard
def has_valid_name(tool_def: dict) -> bool:
return isinstance(tool_def, dict) and isinstance(tool_def.get("name"), str) and bool(tool_def["name"].strip()) Prevention
- Build tool lists with a shared helper that always sets 'name'.
- Validate tool_defs in unit tests before register().
- Pass through real tools/list payloads instead of hand-writing them.
When it happens
Trigger: Calling register(tool_defs, handlers) where a tool dict lacks 'name', has name="" or None, or a non-string value; JSON tool lists where the key was typo'd ('Name', 'tool_name').
Common situations: Building a fake MCP server for tests and forgetting the name field; a proxy renaming keys between wire format and dict; upstream schema change from 'name' to another field.
Related errors
- Every MCP tool needs a non-empty name
- Duplicate MCP tool name on server {self.name!r}
- Missing MCP handlers: {', '.join(missing)}
- MCP tool name is longer than 64 characters: {prefixed}
- Invalid input schema for {origin}
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/bed0e4e22576eae5.
Report an issue: GitHub.