You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: RFC 6570 URI templates with operator-aware security
Replaces the regex-based resource template matcher with a linear-time
RFC 6570 implementation, adds configurable path-safety validation, and
ships a standalone UriTemplate utility usable from the low-level server.
Rebased onto main@4caa41f6 (squashed from 47 commits at 8b5ca89).
Absorbs mcp-types split (#2973), ResourceNotFoundError (#2920),
ResourceError chaining (#2542), resources= ctor param (#2414),
is_async_callable (#2389), and sync-handler offloading (#2380).
ResourceSecurityError now re-raises as ResourceNotFoundError in
ResourceManager.get_resource so security rejections map to -32602
(SEP-2164) and halt template iteration.
Copy file name to clipboardExpand all lines: docs/migration.md
+51Lines changed: 51 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -605,6 +605,57 @@ Reading a missing resource now returns JSON-RPC error code `-32602` (invalid par
605
605
606
606
The underlying lookups now raise typed exceptions instead of `ValueError`. `ResourceManager.get_resource()` raises `ResourceNotFoundError` when no resource or template matches the URI, and `ResourceTemplate.create_resource()` raises `ResourceError` when the template function fails. Neither subclasses `ValueError`, so callers catching `ValueError` should switch to `ResourceNotFoundError` / `ResourceError` (both importable from `mcp.server.mcpserver.exceptions`; `ResourceNotFoundError` subclasses `ResourceError`).
607
607
608
+
### Resource templates: matching behavior changes
609
+
610
+
Resource template matching has been rewritten with RFC 6570 support.
611
+
Several behaviors have changed:
612
+
613
+
**Path-safety checks applied by default.** Extracted parameter values
614
+
containing `..` as a path component, a null byte, or looking like an
615
+
absolute path (`/etc/passwd`, `C:\Windows`) now cause the read to
616
+
fail — the client receives an "Unknown resource" error and template
617
+
iteration stops, so a strict template's rejection does not fall
618
+
through to a later permissive template. This is checked on the
619
+
decoded value, so `..%2Fetc`, `%2E%2E`, and `%00` are caught too.
620
+
Note that `..` is only flagged as a standalone path component, so
621
+
values like `v1.0..v2.0` or `HEAD~3..HEAD` are unaffected.
622
+
623
+
If a parameter legitimately needs to receive absolute paths or
**Template literals and structural delimiters match exactly.** The
637
+
previous matcher built a regex without escaping, so `.` matched any
638
+
character and simple `{var}` swallowed `?`, `#`, `&`, and `,`. Now
639
+
`data://v1.0/{id}` no longer matches `data://v1X0/42`, and
640
+
`api://{id}` no longer matches `api://foo?x=1` — use `api://{id}{?x}`
641
+
or `api://{+id}` if you need to capture a query tail.
642
+
643
+
**Template syntax errors surface at decoration time.** Unclosed
644
+
braces, duplicate variable names, and unsupported syntax raise
645
+
`InvalidUriTemplate` when the decorator runs rather than `re.error`
646
+
on first match.
647
+
648
+
**Static URIs with Context-only handlers now error.** A non-template
649
+
URI paired with a handler that takes only a `Context` parameter
650
+
previously registered but was silently unreachable (the resource
651
+
could never be read). This now raises `ValueError` at decoration time.
652
+
Context injection for static resources is planned; until then, use a
653
+
template with at least one variable or access context through other
654
+
means.
655
+
656
+
See [Resources](server/resources.md) for the full template syntax,
657
+
security configuration, and filesystem safety utilities.
658
+
608
659
### Registering lowlevel handlers from `MCPServer`
609
660
610
661
`MCPServer` does not expose public APIs for `subscribe_resource`, `unsubscribe_resource`, or `set_logging_level` handlers. In v1, the workaround was to reach into the private lowlevel server and use its decorator methods:
0 commit comments