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

  1. Give the tool a non-empty string `name` before calling registerTool
  2. Validate tool objects coming from config/dynamic sources before registering
  3. 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

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


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/82d211852c496b41. Report an issue: GitHub.