Skip to content

Commit b278925

Browse files
committed
feat: add UriTemplate.is_template() static method
Cheap heuristic for distinguishing concrete URIs from templates without full parsing. Returns True if the string contains at least one {...} pair. Does not validate; a True result does not guarantee parse() will succeed. Matches the TypeScript SDK's UriTemplate.isTemplate() utility.
1 parent a463ed9 commit b278925

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

src/mcp/shared/uri_template.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,28 @@ class UriTemplate:
210210
_variables: tuple[Variable, ...] = field(repr=False, compare=False)
211211
_pattern: re.Pattern[str] = field(repr=False, compare=False)
212212

213+
@staticmethod
214+
def is_template(value: str) -> bool:
215+
"""Check whether a string contains URI template expressions.
216+
217+
A cheap heuristic for distinguishing concrete URIs from templates
218+
without the cost of full parsing. Returns ``True`` if the string
219+
contains at least one ``{...}`` pair.
220+
221+
Example::
222+
223+
>>> UriTemplate.is_template("file://docs/{name}")
224+
True
225+
>>> UriTemplate.is_template("file://docs/readme.txt")
226+
False
227+
228+
Note:
229+
This does not validate the template. A ``True`` result does
230+
not guarantee :meth:`parse` will succeed.
231+
"""
232+
open_i = value.find("{")
233+
return open_i != -1 and value.find("}", open_i) != -1
234+
213235
@classmethod
214236
def parse(
215237
cls,

tests/shared/test_uri_template.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,24 @@ def test_parse_literal_only():
1212
assert str(tmpl) == "file://docs/readme.txt"
1313

1414

15+
@pytest.mark.parametrize(
16+
("value", "expected"),
17+
[
18+
("file://docs/{name}", True),
19+
("file://docs/readme.txt", False),
20+
("", False),
21+
("{a}", True),
22+
("{", False),
23+
("}", False),
24+
("}{", False),
25+
("prefix{+path}/suffix", True),
26+
("{invalid syntax but still a template}", True),
27+
],
28+
)
29+
def test_is_template(value: str, expected: bool):
30+
assert UriTemplate.is_template(value) is expected
31+
32+
1533
def test_parse_simple_variable():
1634
tmpl = UriTemplate.parse("file://docs/{name}")
1735
assert tmpl.variables == (Variable(name="name", operator=""),)

0 commit comments

Comments
 (0)