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
@@ -600,6 +600,57 @@ Reading a missing resource now returns JSON-RPC error code `-32602` (invalid par
600
600
601
601
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`).
602
602
603
+
### Resource templates: matching behavior changes
604
+
605
+
Resource template matching has been rewritten with RFC 6570 support.
606
+
Several behaviors have changed:
607
+
608
+
**Path-safety checks applied by default.** Extracted parameter values
609
+
containing `..` as a path component, a null byte, or looking like an
610
+
absolute path (`/etc/passwd`, `C:\Windows`) now cause the read to
611
+
fail — the client receives an "Unknown resource" error and template
612
+
iteration stops, so a strict template's rejection does not fall
613
+
through to a later permissive template. This is checked on the
614
+
decoded value, so `..%2Fetc`, `%2E%2E`, and `%00` are caught too.
615
+
Note that `..` is only flagged as a standalone path component, so
616
+
values like `v1.0..v2.0` or `HEAD~3..HEAD` are unaffected.
617
+
618
+
If a parameter legitimately needs to receive absolute paths or
**Template literals and structural delimiters match exactly.** The
632
+
previous matcher built a regex without escaping, so `.` matched any
633
+
character and simple `{var}` swallowed `?`, `#`, `&`, and `,`. Now
634
+
`data://v1.0/{id}` no longer matches `data://v1X0/42`, and
635
+
`api://{id}` no longer matches `api://foo?x=1` — use `api://{id}{?x}`
636
+
or `api://{+id}` if you need to capture a query tail.
637
+
638
+
**Template syntax errors surface at decoration time.** Unclosed
639
+
braces, duplicate variable names, and unsupported syntax raise
640
+
`InvalidUriTemplate` when the decorator runs rather than `re.error`
641
+
on first match.
642
+
643
+
**Static URIs with Context-only handlers now error.** A non-template
644
+
URI paired with a handler that takes only a `Context` parameter
645
+
previously registered but was silently unreachable (the resource
646
+
could never be read). This now raises `ValueError` at decoration time.
647
+
Context injection for static resources is planned; until then, use a
648
+
template with at least one variable or access context through other
649
+
means.
650
+
651
+
See [Resources](server/resources.md) for the full template syntax,
652
+
security configuration, and filesystem safety utilities.
653
+
603
654
### Registering lowlevel handlers from `MCPServer`
604
655
605
656
`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