github/copilot-sdk · error · ArgumentException
Invalid tool name ' ': tool names must match…
Error message
Invalid {kind} tool name '{name}': tool names must match /^[a-zA-Z0-9_-]+$/ or be the wildcard '*'. What it means
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.
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_-]+$.
Example fix
// before
toolSet.AddMcp("github.com/org/repo:create_issue");
// after
var safe = new string(name.Select(c => char.IsAsciiLetterOrDigit(c) || c is '_' or '-' ? c : '_').ToArray());
toolSet.AddMcp(safe); Defensive patterns
Strategy: validation
Validate before calling
if (name != "*" && !System.Text.RegularExpressions.Regex.IsMatch(name ?? "", "^[a-zA-Z0-9_-]+$"))
throw new ArgumentException($"Invalid tool name '{name}'", nameof(name)); Type guard
static bool IsValidToolName(string? name) =>
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_-]+$"); Try / catch
try { toolSet.AddMcp(name); }
catch (ArgumentException ex) when (ex.Message.Contains("tool names must match"))
{ logger.LogError("Tool name '{Name}' has invalid characters", name); } Prevention
- 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
When it happens
Trigger: 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 "*".
Common situations: 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.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Invalid tool name ' ': tool names must match…
- Invalid mcp tool name
- Invalid tool name: must not be null or empty.
- Invalid mcp tool name: must not be null or empty.
- Exception of type 'System.ObjectDisposedException' was…
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/c96c296bfc746c4e.
Report an issue: GitHub.
Appendix: source
Thrown at dotnet/src/ToolSet.cs:114
Add($"mcp:{toolName}");
return this;
}
private static void ValidateName(string kind, string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(
$"Invalid {kind} tool name: must not be null or empty.",
nameof(name));
}
if (name == "*")
{
return;
}
if (!s_validToolName.IsMatch(name))
{
throw new ArgumentException(
$"Invalid {kind} tool name '{name}': tool names must match /^[a-zA-Z0-9_-]+$/ " +
"or be the wildcard '*'.",
nameof(name));
}
}
}
/// <summary>
/// Curated sets of built-in tool names for common scenarios. Each constant is
/// meant to be passed to <see cref="ToolSet.AddBuiltIn(IEnumerable{string})"/>.
/// </summary>
public static class BuiltInTools
{
/// <summary>
/// Built-in tools that operate only within the bounds of a single session
/// — no host filesystem access outside the session, no cross-session
/// state, no host environment access, no network. Safe to enable in
/// <see cref="CopilotClientMode.Empty"/> scenarios (e.g. multi-tenantView on GitHub (pinned to cd8cf15dc3)