Jeremiah Lowin commited on
Commit
2e6dbad
·
1 Parent(s): 24b96b7

Improve matching logic for quoted chars

Browse files
src/fastmcp/resources/template.py CHANGED
@@ -6,6 +6,7 @@ import inspect
6
  import re
7
  from collections.abc import Callable
8
  from typing import Annotated, Any
 
9
 
10
  from mcp.types import ResourceTemplate as MCPResourceTemplate
11
  from pydantic import (
@@ -38,7 +39,9 @@ def build_regex(template: str) -> re.Pattern:
38
  def match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None:
39
  regex = build_regex(uri_template)
40
  match = regex.match(uri)
41
- return match.groupdict() if match else None
 
 
42
 
43
 
44
  class MyModel(BaseModel):
 
6
  import re
7
  from collections.abc import Callable
8
  from typing import Annotated, Any
9
+ from urllib.parse import unquote
10
 
11
  from mcp.types import ResourceTemplate as MCPResourceTemplate
12
  from pydantic import (
 
39
  def match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None:
40
  regex = build_regex(uri_template)
41
  match = regex.match(uri)
42
+ if match:
43
+ return {k: unquote(v) for k, v in match.groupdict().items()}
44
+ return None
45
 
46
 
47
  class MyModel(BaseModel):
tests/resources/test_resource_template.py CHANGED
@@ -1,4 +1,5 @@
1
  import json
 
2
 
3
  import pytest
4
  from pydantic import BaseModel
@@ -312,6 +313,18 @@ class TestMatchUriTemplate:
312
  ("test://foo/123", {"x": "foo", "y": "123"}),
313
  ("test://bar/456", {"x": "bar", "y": "456"}),
314
  ("test://foo/bar", {"x": "foo", "y": "bar"}),
 
 
 
 
 
 
 
 
 
 
 
 
315
  ("prefix+test://foo/123", None),
316
  ("test://foo", None),
317
  ("other://foo/123", None),
 
1
  import json
2
+ from urllib.parse import quote
3
 
4
  import pytest
5
  from pydantic import BaseModel
 
313
  ("test://foo/123", {"x": "foo", "y": "123"}),
314
  ("test://bar/456", {"x": "bar", "y": "456"}),
315
  ("test://foo/bar", {"x": "foo", "y": "bar"}),
316
+ ("test://foo/bar/baz", None),
317
+ ("test://foo/email@domain.com", {"x": "foo", "y": "email@domain.com"}),
318
+ ("test://two words/foo", {"x": "two words", "y": "foo"}),
319
+ ("test://two.words/foo+bar", {"x": "two.words", "y": "foo+bar"}),
320
+ (
321
+ f"test://escaped{quote('/', safe='')}word/bar",
322
+ {"x": "escaped/word", "y": "bar"},
323
+ ),
324
+ (
325
+ f"test://escaped{quote('{', safe='')}x{quote('}', safe='')}word/bar",
326
+ {"x": "escaped{x}word", "y": "bar"},
327
+ ),
328
  ("prefix+test://foo/123", None),
329
  ("test://foo", None),
330
  ("other://foo/123", None),