Skip to content

Commit 780425a

Browse files
committed
fix(uri-template): encode literal URI characters
1 parent 6affe5c commit 780425a

2 files changed

Lines changed: 12 additions & 1 deletion

File tree

‎src/mcp/shared/uri_template.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ def expand(self, variables: Mapping[str, str | Sequence[str]]) -> str:
451451
out: list[str] = []
452452
for part in self._parts:
453453
if isinstance(part, str):
454-
out.append(part)
454+
out.append(_encode(part, allow_reserved=True))
455455
else:
456456
out.append(_expand_expression(part, variables))
457457
return "".join(out)
@@ -892,6 +892,7 @@ def _flatten(parts: list[_Part]) -> list[_Atom]:
892892
def push_lit(text: str) -> None:
893893
if not text:
894894
return
895+
text = _encode(text, allow_reserved=True)
895896
if atoms and isinstance(atoms[-1], _Lit):
896897
atoms[-1] = _Lit(atoms[-1].text + text)
897898
else:

‎tests/shared/test_uri_template.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,11 @@ def test_expand_encodes_special_chars_in_simple():
479479
assert t.expand({"v": "a&b=c"}) == "a%26b%3Dc"
480480

481481

482+
def test_expand_encodes_non_ascii_literal():
483+
t = UriTemplate.parse("file:///docs/café/{name}")
484+
assert t.expand({"name": "a.txt"}) == "file:///docs/caf%C3%A9/a.txt"
485+
486+
482487
def test_expand_preserves_special_chars_in_reserved():
483488
t = UriTemplate.parse("{+v}")
484489
assert t.expand({"v": "a&b=c"}) == "a&b=c"
@@ -764,6 +769,11 @@ def test_match_decodes_percent_encoding():
764769
assert t.match("file://docs/hello%20world.txt") == {"name": "hello world.txt"}
765770

766771

772+
def test_match_encodes_non_ascii_literal():
773+
t = UriTemplate.parse("file:///docs/café/{name}")
774+
assert t.match("file:///docs/caf%C3%A9/a.txt") == {"name": "a.txt"}
775+
776+
767777
def test_match_escapes_template_literals():
768778
# Regression: previous impl didn't escape . in literals, making it
769779
# a regex wildcard. "fileXtxt" should NOT match "file.txt/{id}".

0 commit comments

Comments
 (0)