Skip to content

Commit 2adb6f0

Browse files
committed
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.
1 parent 3caa445 commit 2adb6f0

13 files changed

Lines changed: 3093 additions & 37 deletions

File tree

docs/migration.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,57 @@ Reading a missing resource now returns JSON-RPC error code `-32602` (invalid par
605605

606606
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`).
607607

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
624+
traversal sequences, exempt it:
625+
626+
```python
627+
from mcp.server.mcpserver import ResourceSecurity
628+
629+
@mcp.resource(
630+
"inspect://file/{+target}",
631+
security=ResourceSecurity(exempt_params={"target"}),
632+
)
633+
def inspect_file(target: str) -> str: ...
634+
```
635+
636+
**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+
608659
### Registering lowlevel handlers from `MCPServer`
609660

610661
`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

Comments
 (0)