Skip to content

Commit 891b00b

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 411a6d3 commit 891b00b

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
@@ -600,6 +600,57 @@ Reading a missing resource now returns JSON-RPC error code `-32602` (invalid par
600600

601601
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`).
602602

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
619+
traversal sequences, exempt it:
620+
621+
```python
622+
from mcp.server.mcpserver import ResourceSecurity
623+
624+
@mcp.resource(
625+
"inspect://file/{+target}",
626+
security=ResourceSecurity(exempt_params={"target"}),
627+
)
628+
def inspect_file(target: str) -> str: ...
629+
```
630+
631+
**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+
603654
### Registering lowlevel handlers from `MCPServer`
604655

605656
`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)