neoclide/coc.nvim · error
Tool name is required
Error message
Tool name is required
What it means
`McpServer.registerTool` validates that the tool object has a non-empty string `name` before registering it with the tool registry. Registering an unnamed or malformed tool would break MCP tool listing and dispatch, so it is rejected eagerly.
Source
Thrown at src/mcp/index.ts:256
workspace.nvim.setVar('coc_mcp_started', 0, true)
}
logger.info('MCP server stopped')
}
public dispose(): void {
this.stop()
}
/**
* Register a custom MCP tool from a coc.nvim extension. The tool becomes
* available on the next `tools/list` (immediately when the server is
* running, via `notifications/tools/list_changed`). Returns a Disposable
* that unregisters the tool.
*/
public registerTool(tool: McpTool): Disposable {
let name = tool?.name
if (typeof name !== 'string' || name.length === 0) {
throw new Error('Tool name is required')
}
return this.getRegistry().register(tool)
}
private getRegistry(): ToolRegistry {
if (!this.registry) {
let registry = new ToolRegistry()
for (let tool of [...createWorkspaceTools(), ...createDocumentTools(), ...createLspTools(), ...createEditorTools()]) {
registry.register(tool)
}
this.registry = registry
}
return this.registry
}
private getConfig(): McpConfig {
let config = workspace.getConfiguration('mcp')
return {View on GitHub (pinned to 50e974d969)
Solutions
- Give the tool a non-empty string `name` before calling registerTool
- Validate tool objects coming from config/dynamic sources before registering
- Log the tool object on failure to find which construction path omitted the name
Example fix
// before
server.registerTool({ handler: async () => ({}) })
// after
server.registerTool({ name: 'my-tool', handler: async () => ({}) }) Defensive patterns
Strategy: validation
Validate before calling
function isRegisterableTool(tool: unknown): tool is McpTool {
return !!tool && typeof tool === 'object' &&
typeof (tool as McpTool).name === 'string' && (tool as McpTool).name.length > 0 &&
typeof (tool as McpTool).handler === 'function'
} Type guard
function hasName(t: unknown): t is McpTool & { name: string } {
return typeof t === 'object' && t !== null &&
typeof (t as { name?: unknown }).name === 'string' &&
(t as { name: string }).name.length > 0
} Try / catch
try {
disposables.push(server.registerTool(tool))
} catch (e) {
logger.error('registerTool rejected:', JSON.stringify(tool))
throw e
} Prevention
- Always define a non-empty string name on McpTool objects
- Validate dynamically-generated tool objects before registration
- Type tool definitions with the McpTool interface instead of plain literals in JS
- Log the full tool object when registration fails
When it happens
Trigger: Calling `registerTool(undefined)`, `registerTool({})`, `registerTool({ name: '' })`, or passing a tool whose `name` is not a string (e.g. `name: 42` via untyped JS).
Common situations: Hand-writing an McpTool object and forgetting the name field; dynamically constructing tools from config/JSON where the name key is missing or empty after trimming; a factory function returning undefined on an error path that is passed straight to registerTool.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- name and doComplete required for createSource
- Feature param could only starts with nvim and patch
- Invalid key ${name} of registerKeymap
- Invalid extension name: ${name}
- Invalid pattern ${filter.pattern.glob}!
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/82d211852c496b41.
Report an issue: GitHub.