Describe the bug
TOOL_NAME_REGEX in src/mcp/shared/tool_name_validation.py is end-anchored with $:
TOOL_NAME_REGEX = re.compile(r"^[A-Za-z0-9._-]{1,128}$")
In Python's default (non-MULTILINE) mode, $ matches at end-of-string or immediately before a single trailing \n. So a tool name ending in exactly one newline passes validation, even though \n is not in the allowed character set.
The length guard uses len(), which counts the \n, so "a" * 127 + "\n" (length 128) slips past both the length check and the character check.
Embedded newlines and other trailing control characters are already rejected correctly. Only the single-trailing-newline case leaks.
To Reproduce
from mcp.shared.tool_name_validation import validate_tool_name, validate_and_warn_tool_name
print(validate_tool_name("evil_tool\n").is_valid) # True (expected: False)
print(validate_and_warn_tool_name("evil_tool\n")) # True, and no warning is logged
print(validate_tool_name("a" * 127 + "\n").is_valid) # True, length 128 passes too
Expected behavior
validate_tool_name("evil_tool\n").is_valid should be False, since \n is not one of the allowed characters.
Additional context
Anchoring the end with \Z (strict end-of-string) instead of $ fixes it, and no valid name is affected: re.match(r"^[A-Za-z0-9._-]{1,128}\Z", "abc") still matches, while "abc\n" correctly fails.
I have an open PR with this fix and a regression test: #3076. Filing this issue to track it, per CONTRIBUTING ("Bug fixes for clear, reproducible issues are welcome—but still create an issue to track the fix").
Describe the bug
TOOL_NAME_REGEXinsrc/mcp/shared/tool_name_validation.pyis end-anchored with$:In Python's default (non-
MULTILINE) mode,$matches at end-of-string or immediately before a single trailing\n. So a tool name ending in exactly one newline passes validation, even though\nis not in the allowed character set.The length guard uses
len(), which counts the\n, so"a" * 127 + "\n"(length 128) slips past both the length check and the character check.Embedded newlines and other trailing control characters are already rejected correctly. Only the single-trailing-newline case leaks.
To Reproduce
Expected behavior
validate_tool_name("evil_tool\n").is_validshould beFalse, since\nis not one of the allowed characters.Additional context
Anchoring the end with
\Z(strict end-of-string) instead of$fixes it, and no valid name is affected:re.match(r"^[A-Za-z0-9._-]{1,128}\Z", "abc")still matches, while"abc\n"correctly fails.I have an open PR with this fix and a regression test: #3076. Filing this issue to track it, per CONTRIBUTING ("Bug fixes for clear, reproducible issues are welcome—but still create an issue to track the fix").