{"record":{"id":"c83f75e353dae55b","repo":"github/copilot-sdk","slug":"invalid-kind-tool-name-name-tool-names-mu","errorCode":null,"errorMessage":"Invalid ${kind} tool name '${name}': tool names must match /^[a-zA-Z0-9_-]+$/ or be the wildcard '*'.","messagePattern":"Invalid (.+?) tool name '(.+?)': tool names must match /\\^\\[a-zA-Z0-9_-\\]\\+\\$/ or be the wildcard '\\*'\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodejs/src/toolSet.ts","lineNumber":25,"sourceCode":" * source-qualified filter patterns (`builtin:*`, `mcp:<name>`, `custom:*`, etc.).\n *\n * See plan: client-level Mode = \"empty\" with explicit tool selection.\n */\n\n/**\n * Tool name character set enforced by the runtime at every registration\n * boundary. Mirrors the runtime's `VALID_TOOL_NAME_REGEX`. Used to validate\n * names passed to the `ToolSet` builder so misuse is caught at the SDK\n * boundary with a better error than the runtime would produce.\n */\nconst VALID_TOOL_NAME = /^[a-zA-Z0-9_-]+$/;\n\nfunction validateName(kind: \"builtin\" | \"mcp\" | \"custom\", name: string): void {\n    if (name === \"*\") {\n        return;\n    }\n    if (!VALID_TOOL_NAME.test(name)) {\n        throw new Error(\n            `Invalid ${kind} tool name '${name}': tool names must match /^[a-zA-Z0-9_-]+$/ ` +\n                `or be the wildcard '*'.`\n        );\n    }\n}\n\n/**\n * Builder that produces a list of source-qualified tool filter strings for\n * {@link SessionConfigBase.availableTools}.\n *\n * Tools are classified by the runtime at registration time (not from name\n * parsing), so `addBuiltIn(\"foo\")` matches only tools the runtime registered\n * as built-in, even if an MCP server or custom-agent extension happens to\n * register a tool with the same wire name.\n *\n * @example\n * ```typescript\n * const tools = new ToolSet()","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/toolSet.ts#L7-L43","documentation":"Thrown by validateName in toolSet.ts when registering a builtin, mcp, or custom tool whose name is neither the wildcard \"*\" nor matches /^[a-zA-Z0-9_-]+$/. The toolset enforces a strict identifier charset so tool names can be safely referenced and dispatched.","triggerScenarios":"Calling addBuiltIn/addCustom/addMcp with a name containing spaces, dots, slashes, colons, or non-ASCII characters; passing a fully-qualified MCP name like \"server.tool\" or a path-like name; passing an empty string or undefined coerced to a string.","commonSituations":"Registering MCP tools with vendor-prefixed names containing dots or colons; deriving tool names from file paths or URLs; migrating tool sets that previously allowed dotted names.","solutions":["Sanitize the tool name to only [a-zA-Z0-9_-] before registering (e.g. replace invalid chars with \"_\")","Use the wildcard \"*\" only when you intentionally mean all tools","Fix the name string so it matches /^[a-zA-Z0-9_-]+$/"],"exampleFix":"// before\ntoolSet.addMcp(\"github.com:search-issues\"); // invalid ':' and '.'\n// after\nconst safe = \"github.com:search-issues\".replace(/[^a-zA-Z0-9_-]/g, \"_\"); // github_com_search-issues\ntoolSet.addMcp(safe);","handlingStrategy":"validation","validationCode":"const VALID_TOOL_NAME = /^[a-zA-Z0-9_-]+$/;\nif (name !== \"*\" && !VALID_TOOL_NAME.test(name)) {\n  throw new Error(`Invalid tool name before registration: ${name}`);\n}","typeGuard":"function isValidToolName(name) {\n  return name === \"*\" || /^[a-zA-Z0-9_-]+$/.test(name);\n}","tryCatchPattern":"try {\n  toolSet.addMcp(rawName);\n} catch (err) {\n  if (String(err?.message).startsWith(\"Invalid \")) {\n    toolSet.addMcp(rawName.replace(/[^a-zA-Z0-9_-]/g, \"_\"));\n  } else throw err;\n}","preventionTips":["Sanitize names derived from MCP server ids, paths, or URLs before registration","Validate tool names in CI with a unit test against /^[a-zA-Z0-9_-]+$/","Reserve \"*\" for wildcard semantics only"],"tags":["toolset","validation","naming"],"backgroundTag":"invalid-identifier-format","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}