{"record":{"id":"c96c296bfc746c4e","repo":"github/copilot-sdk","slug":"invalid-kind-tool-name-name-tool-names-must","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":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"dotnet/src/ToolSet.cs","lineNumber":114,"sourceCode":"        Add($\"mcp:{toolName}\");\n        return this;\n    }\n\n    private static void ValidateName(string kind, string name)\n    {\n        if (string.IsNullOrEmpty(name))\n        {\n            throw new ArgumentException(\n                $\"Invalid {kind} tool name: must not be null or empty.\",\n                nameof(name));\n        }\n        if (name == \"*\")\n        {\n            return;\n        }\n        if (!s_validToolName.IsMatch(name))\n        {\n            throw new ArgumentException(\n                $\"Invalid {kind} tool name '{name}': tool names must match /^[a-zA-Z0-9_-]+$/ \" +\n                \"or be the wildcard '*'.\",\n                nameof(name));\n        }\n    }\n}\n\n/// <summary>\n/// Curated sets of built-in tool names for common scenarios. Each constant is\n/// meant to be passed to <see cref=\"ToolSet.AddBuiltIn(IEnumerable{string})\"/>.\n/// </summary>\npublic static class BuiltInTools\n{\n    /// <summary>\n    /// Built-in tools that operate only within the bounds of a single session\n    /// — no host filesystem access outside the session, no cross-session\n    /// state, no host environment access, no network. Safe to enable in\n    /// <see cref=\"CopilotClientMode.Empty\"/> scenarios (e.g. multi-tenant","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/dotnet/src/ToolSet.cs#L96-L132","documentation":"ToolSet.ValidateName also enforces the character set for tool names: a name must match ^[a-zA-Z0-9_-]+$ or be the literal wildcard \"*\". Names containing dots, slashes, spaces, colons, or other characters are rejected with ArgumentException. This normalization keeps tool names valid across MCP and filter semantics.","triggerScenarios":"Adding a tool whose name contains characters outside [a-zA-Z0-9_-], e.g. AddMcp(\"github.com/server/create_issue\") or AddCustom(\"my tool\"), unless the exact name is \"*\".","commonSituations":"MCP server tool ids arriving with server prefixes or dots; converting display labels (with spaces/slashes) into tool names; generating names from URLs or file paths.","solutions":["Sanitize the name to the allowed charset: replace invalid characters with '_' or '-' before calling Add*.","Strip server/namespace prefixes (e.g. everything before '/') so only the base tool name is registered.","If wildcard semantics are intended, pass the literal \"*\" instead of a pattern like '*.write'.","Catch ArgumentException and report which name failed the pattern ^[a-zA-Z0-9_-]+$."],"exampleFix":"// before\ntoolSet.AddMcp(\"github.com/org/repo:create_issue\");\n\n// after\nvar safe = new string(name.Select(c => char.IsAsciiLetterOrDigit(c) || c is '_' or '-' ? c : '_').ToArray());\ntoolSet.AddMcp(safe);","handlingStrategy":"validation","validationCode":"if (name != \"*\" && !System.Text.RegularExpressions.Regex.IsMatch(name ?? \"\", \"^[a-zA-Z0-9_-]+$\"))\n    throw new ArgumentException($\"Invalid tool name '{name}'\", nameof(name));","typeGuard":"static bool IsValidToolName(string? name) =>\n    name == \"*\" || (name != null && System.Text.RegularExpressions.Regex.IsMatch(name, \"^[a-zA-Z0-9_-]+$(?<=^[a-zA-Z0-9_-]+$)\")) is true && System.Text.RegularExpressions.Regex.IsMatch(name, \"^[a-zA-Z0-9_-]+$\");","tryCatchPattern":"try { toolSet.AddMcp(name); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"tool names must match\"))\n{ logger.LogError(\"Tool name '{Name}' has invalid characters\", name); }","preventionTips":["Sanitize names to [a-zA-Z0-9_-] before registration","Strip server prefixes/dots from MCP tool ids","Use the literal \"*\" for wildcard rather than pattern strings"],"tags":["toolset","naming","regex-validation","dotnet"],"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"}