Skip to content

Commit 1216c53

Browse files
authored
fix: reject trailing newline in tool-name and URI-template varname validation (#3076)
Python's $ with re.match also matches just before a single trailing newline, so tool-name validation accepted "name\n" and UriTemplate.parse accepted varnames like "foo\n". Switch both checks to re.fullmatch. Closes #3084
1 parent 4fc8882 commit 1216c53

4 files changed

Lines changed: 9 additions & 2 deletions

File tree

src/mcp/shared/tool_name_validation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def validate_tool_name(name: str) -> ToolNameValidationResult:
7777
warnings.append("Tool name starts or ends with a dot, which may cause parsing issues in some contexts")
7878

7979
# Check for invalid characters
80-
if not TOOL_NAME_REGEX.match(name):
80+
if not TOOL_NAME_REGEX.fullmatch(name):
8181
# Find all invalid characters (unique, preserving order)
8282
invalid_chars: list[str] = []
8383
seen: set[str] = set()

src/mcp/shared/uri_template.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ def _parse_expression(template: str, body: str, pos: int) -> _Expression:
813813
explode = spec.endswith("*")
814814
name = spec[:-1] if explode else spec
815815

816-
if not _VARNAME_RE.match(name):
816+
if not _VARNAME_RE.fullmatch(name):
817817
raise InvalidUriTemplate(
818818
f"Invalid variable name {name!r} at position {pos}",
819819
template=template,

tests/shared/test_tool_name_validation.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,17 @@ def test_validate_tool_name_rejects_name_exceeding_max_length() -> None:
6565
("get,user,profile", "','"),
6666
("user/profile/update", "'/'"),
6767
("user@domain.com", "'@'"),
68+
# a single trailing newline slipped past `$` with re.match
69+
("valid_name\n", "'\\n'"),
70+
("a" * 127 + "\n", "'\\n'"),
6871
],
6972
ids=[
7073
"with_spaces",
7174
"with_commas",
7275
"with_slashes",
7376
"with_at_symbol",
77+
"with_trailing_newline",
78+
"max_length_with_trailing_newline",
7479
],
7580
)
7681
def test_validate_tool_name_rejects_invalid_characters(tool_name: str, expected_char: str) -> None:

tests/shared/test_uri_template.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ def test_parse_rejects_operator_without_variable():
144144
# RFC §2.3: dots only between varchars, not consecutive or trailing
145145
"foo..bar",
146146
"foo.",
147+
# a single trailing newline slipped past `$` with re.match
148+
"foo\n",
147149
],
148150
)
149151
def test_parse_rejects_invalid_varname(name: str):

0 commit comments

Comments
 (0)