Changes the type from frozenset[str] to collections.abc.Set[str] so
users can write exempt_params={"range"} instead of
exempt_params=frozenset({"range"}). The default factory stays
frozenset for immutability.
Adds `resource_security` to `MCPServer.__init__` and a per-resource
`security` override to the `@resource()` decorator. Templates inherit
the server-wide policy unless overridden.
Exports `ResourceSecurity` and `DEFAULT_RESOURCE_SECURITY` from
`mcp.server.mcpserver` for user configuration.
Usage:
# Server-wide relaxation
mcp = MCPServer(resource_security=ResourceSecurity(reject_path_traversal=False))
# Per-resource exemption for non-path parameters
@mcp.resource(
"git://diff/{+range}",
security=ResourceSecurity(exempt_params=frozenset({"range"})),
)
def git_diff(range: str) -> str: ...
Refactors the internal `ResourceTemplate` to use the RFC 6570
`UriTemplate` engine for matching, and adds a configurable
`ResourceSecurity` policy for path-safety checks on extracted
parameters.
`ResourceTemplate.matches()` now:
- Delegates to `UriTemplate.match()` for full RFC 6570 Level 1-3
support (plus path-style explode). `{+path}` can match
multi-segment paths.
- Enforces structural integrity: `%2F` smuggled into a simple
`{var}` is rejected.
- Applies `ResourceSecurity` policy: path traversal (`..` components)
and absolute paths rejected by default, with per-parameter
exemption available.
The `@mcp.resource()` decorator now parses the template once at
decoration time via `UriTemplate.parse()`, replacing the regex-based
param extraction that couldn't handle operators like `{+path}`.
Malformed templates surface immediately with a clear
`InvalidUriTemplate` including position info.
Also fixes the pre-existing bug where template literals were not
regex-escaped (a `.` in the template acted as a wildcard).
Adds `mcp.shared.path_security` with three standalone utilities for
defending against path-traversal attacks when URI template parameters
flow into filesystem operations:
- `contains_path_traversal()` — base-free component-level check for
`..` escapes, handles both `/` and `\` separators
- `is_absolute_path()` — detects POSIX, Windows drive, and UNC
absolute paths (which silently discard the base in `Path` joins)
- `safe_join()` — resolve-and-verify within a sandbox root; catches
`..`, absolute injection, and symlink escapes
These are pure functions usable from both MCPServer and lowlevel
server implementations. `PathEscapeError(ValueError)` is raised by
`safe_join` on violation.
Adds `mcp.shared.uri_template.UriTemplate`, a standalone utility for
parsing, expanding, and matching RFC 6570 URI templates. Supports
Levels 1-3 fully plus path-style explode (`{/var*}`, `{.var*}`,
`{;var*}`).
Matching enforces structural integrity: decoded values are validated
against their operator's permitted character set. A simple `{var}`
whose decoded value contains `/` is rejected, preventing `%2F`
smuggling while still allowing `/` in `{+var}` where it is
intentional. This is the operator-aware generalization of the
post-decode check for encoded path separators.
Also fixes the existing regex-escaping gap where template literals
like `.` were treated as regex wildcards.
The utility lives in `shared/` so it is usable from both client code
(expand) and server code (match), including lowlevel server
implementations that do not use MCPServer.