github/copilot-sdk · error · IllegalArgumentException

Invalid mcp tool name

Error message

Invalid mcp tool name '<name>': tool names must match /^[a-zA-Z0-9_-]+$/ or be the wildcard '*'. (template: Invalid <kind> tool name '<name>': tool names must match /^[a-zA-Z0-9_-]+$/ or be the wildcard '*'.)

What it means

ToolSet names must match /^[a-zA-Z0-9_-]+$/ or be the wildcard "*". validateName throws this IllegalArgumentException when a name is non-empty but contains illegal characters (e.g. dots, colons, spaces, slashes), because such names cannot be matched against tool identifiers.

Solutions

  1. Sanitize the name to allowed characters (letters, digits, underscore, hyphen) before adding.
  2. Strip prefixes/qualifiers so only the bare tool name is passed (e.g. 'myserver_mytool' rather than 'my.server/mytool').
  3. Use the exact string "*" (no whitespace) when you intend to allow all tools.
  4. Validate names against /^[a-zA-Z0-9_-]+$/ in your config-loading code and fail with a clear message.

Example fix

// before
set.addMcp("github.com/tools/search"); // illegal characters

// after
set.addMcp("github-com-tools-search"); // sanitized, matches /^[a-zA-Z0-9_-]+$/
Defensive patterns

Strategy: validation

Validate before calling

static final Pattern OK = Pattern.compile("^[a-zA-Z0-9_-]+$");
if (name == null || (!name.equals("*") && !OK.matcher(name).matches()))
  throw new IllegalArgumentException("illegal tool name: " + name);

Type guard

static boolean isAllowedToolName(String n) {
  return "*".equals(n) || (n != null && n.matches("^[a-zA-Z0-9_-]+$"));
}

Try / catch

try { set.addMcp(rawName); } catch (IllegalArgumentException e) {
  set.addMcp(rawName.replaceAll("[^a-zA-Z0-9_-]", "-")); // sanitized retry
}

Prevention

When it happens

Trigger: Calling toolSet.addMcp("my.server.tool"), addBuiltIn("search docs"), addCustom("fs/read"), or any name with characters outside [a-zA-Z0-9_-]. Passing a prefixed identifier like "mcp:foo" instead of the bare name "foo".

Common situations: Passing MCP server identifiers with dots/colons (FQDNs, URIs) instead of the registered tool name; including namespace prefixes like 'server:tool'; names with spaces copied from documentation; accidentally passing the wildcard with surrounding whitespace.

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


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/ae50b25d094b32b2. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/rpc/ToolSet.java:118

     * @return this {@code ToolSet} for chaining
     * @throws IllegalArgumentException
     *             if toolName is null, empty, or contains invalid characters
     */
    public ToolSet addMcp(String toolName) {
        validateName("mcp", toolName);
        add("mcp:" + toolName);
        return this;
    }

    private static void validateName(String kind, String name) {
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("Invalid " + kind + " tool name: must not be null or empty.");
        }
        if ("*".equals(name)) {
            return;
        }
        if (!VALID_TOOL_NAME.matcher(name).matches()) {
            throw new IllegalArgumentException("Invalid " + kind + " tool name '" + name
                    + "': tool names must match /^[a-zA-Z0-9_-]+$/ or be the wildcard '*'.");
        }
    }
}

View on GitHub (pinned to cd8cf15dc3)