Skip to content

Commit c017c89

Browse files
test: reproduce #973
Github-Issue: #973
1 parent f2b89ec commit c017c89

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Test that URL-encoded parameters are decoded in resource templates.
2+
3+
Regression test for https://github.com/modelcontextprotocol/python-sdk/issues/973
4+
"""
5+
6+
from mcp.server.fastmcp.resources import ResourceTemplate
7+
8+
9+
class TestUrlParameterDecoding:
10+
"""Test URL parameter decoding in resource templates."""
11+
12+
def test_template_matches_decodes_space(self):
13+
"""Test that %20 is decoded to space."""
14+
15+
def search(query: str) -> str: # pragma: no cover
16+
return f"Results for: {query}"
17+
18+
template = ResourceTemplate.from_function(
19+
fn=search,
20+
uri_template="search://{query}",
21+
name="search",
22+
)
23+
24+
params = template.matches("search://hello%20world")
25+
assert params is not None
26+
assert params["query"] == "hello world"
27+
28+
def test_template_matches_decodes_accented_characters(self):
29+
"""Test that %C3%A9 is decoded to e with accent."""
30+
31+
def search(query: str) -> str: # pragma: no cover
32+
return f"Results for: {query}"
33+
34+
template = ResourceTemplate.from_function(
35+
fn=search,
36+
uri_template="search://{query}",
37+
name="search",
38+
)
39+
40+
params = template.matches("search://caf%C3%A9")
41+
assert params is not None
42+
assert params["query"] == "cafe" # encoded as UTF-8
43+
44+
def test_template_matches_decodes_complex_phrase(self):
45+
"""Test complex French phrase from the original issue."""
46+
47+
def search(query: str) -> str: # pragma: no cover
48+
return f"Results for: {query}"
49+
50+
template = ResourceTemplate.from_function(
51+
fn=search,
52+
uri_template="search://{query}",
53+
name="search",
54+
)
55+
56+
params = template.matches(
57+
"search://stick%20correcteur%20teint%C3%A9%20anti-imperfections"
58+
)
59+
assert params is not None
60+
assert params["query"] == "stick correcteur teinte anti-imperfections"
61+
62+
def test_template_matches_preserves_plus_sign(self):
63+
"""Test that plus sign remains as plus (not converted to space).
64+
65+
In URI encoding, %20 is space. Plus-as-space is only for
66+
application/x-www-form-urlencoded (HTML forms).
67+
"""
68+
69+
def search(query: str) -> str: # pragma: no cover
70+
return f"Results for: {query}"
71+
72+
template = ResourceTemplate.from_function(
73+
fn=search,
74+
uri_template="search://{query}",
75+
name="search",
76+
)
77+
78+
params = template.matches("search://hello+world")
79+
assert params is not None
80+
assert params["query"] == "hello+world"

0 commit comments

Comments
 (0)