From 376a4a57e293c87d4d2c5258182c7c62253c347e Mon Sep 17 00:00:00 2001 From: Bill Easton Date: Fri, 27 Mar 2026 20:41:56 -0500 Subject: [PATCH] Docs: add FileTreeStore sanitization warnings and update examples (#3661) Co-authored-by: Marvin Context Protocol <41898282+Marvin Context Protocol@users.noreply.github.com> Co-authored-by: Bill Easton --- docs/getting-started/upgrading/from-fastmcp-2.mdx | 8 ++++++-- docs/servers/middleware.mdx | 14 ++++++++++++-- docs/servers/storage-backends.mdx | 4 +++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/getting-started/upgrading/from-fastmcp-2.mdx b/docs/getting-started/upgrading/from-fastmcp-2.mdx index 47baa5655..f4795fe15 100644 --- a/docs/getting-started/upgrading/from-fastmcp-2.mdx +++ b/docs/getting-started/upgrading/from-fastmcp-2.mdx @@ -77,7 +77,7 @@ BREAKING CHANGES (will crash at import or runtime): 10. DECORATORS: @mcp.tool, @mcp.resource, @mcp.prompt now return the original function, not a component object. Code that accesses .name, .description, or other component attributes on the decorated result will crash with AttributeError. Fix: set FASTMCP_DECORATOR_MODE=object for v2 compat (itself deprecated). -11. OAUTH STORAGE: Default OAuth client storage changed from DiskStore to FileTreeStore due to pickle deserialization vulnerability in diskcache (CVE-2025-69872). Clients using default storage will re-register automatically on first connection. If using DiskStore explicitly, switch to FileTreeStore or add pip install 'py-key-value-aio[disk]'. +11. OAUTH STORAGE: Default OAuth client storage changed from DiskStore to FileTreeStore due to pickle deserialization vulnerability in diskcache (CVE-2025-69872). Clients using default storage will re-register automatically on first connection. If using DiskStore explicitly, switch to FileTreeStore (with key/collection sanitization strategies) or add pip install 'py-key-value-aio[disk]'. 12. REPO MOVE: GitHub repository moved from jlowin/fastmcp to PrefectHQ/fastmcp. Update git remotes and dependency URLs that reference the old location. @@ -126,7 +126,11 @@ The default OAuth client storage has moved from `DiskStore` to `FileTreeStore` t If you were using the default storage (i.e., not passing an explicit `client_storage`), clients will need to re-register on their first connection after upgrading. This happens automatically — no user action required, and it's the same flow that already occurs whenever a server restarts with in-memory storage. -If you were passing a `DiskStore` explicitly, you can either [switch to `FileTreeStore`](/servers/storage-backends) (recommended) or keep using `DiskStore` by adding the dependency yourself: +If you were passing a `DiskStore` explicitly, you can either [switch to `FileTreeStore`](/servers/storage-backends) (recommended) or keep using `DiskStore` by adding the dependency yourself. + + +When switching to `FileTreeStore`, you **must** configure key and collection sanitization strategies. Without them, keys containing special characters (such as URL-based OAuth client IDs) will cause filesystem errors. See the [File Storage](/servers/storage-backends#file-storage) section for the recommended setup. + Keeping `DiskStore` requires `pip install 'py-key-value-aio[disk]'`, which re-introduces the vulnerable `diskcache` package into your dependency tree. diff --git a/docs/servers/middleware.mdx b/docs/servers/middleware.mdx index 424f5afa7..1a18d89e3 100644 --- a/docs/servers/middleware.mdx +++ b/docs/servers/middleware.mdx @@ -421,11 +421,21 @@ Each settings class accepts: For persistence or distributed deployments, configure a different storage backend: ```python +from pathlib import Path from fastmcp.server.middleware.caching import ResponseCachingMiddleware -from key_value.aio.stores.disk import DiskStore +from key_value.aio.stores.filetree import ( + FileTreeStore, + FileTreeV1KeySanitizationStrategy, + FileTreeV1CollectionSanitizationStrategy, +) +cache_dir = Path("cache") mcp.add_middleware(ResponseCachingMiddleware( - cache_storage=DiskStore(directory="cache") + cache_storage=FileTreeStore( + data_directory=cache_dir, + key_sanitization_strategy=FileTreeV1KeySanitizationStrategy(cache_dir), + collection_sanitization_strategy=FileTreeV1CollectionSanitizationStrategy(cache_dir), + ) )) ``` diff --git a/docs/servers/storage-backends.mdx b/docs/servers/storage-backends.mdx index 1bdb19b20..e743b5dab 100644 --- a/docs/servers/storage-backends.mdx +++ b/docs/servers/storage-backends.mdx @@ -64,7 +64,9 @@ store = FileTreeStore( middleware = ResponseCachingMiddleware(cache_storage=store) ``` -The sanitization strategies ensure keys and collection names are safe for the filesystem — alphanumeric names pass through as-is for readability, while special characters are hashed to prevent path traversal. + +**Sanitization strategies are required** when using `FileTreeStore`. Without them, keys containing special characters (such as URL-based OAuth client IDs like `https://claude.ai/oauth/claude-code-client-metadata`) will be used as-is in filesystem paths, causing `FileNotFoundError` crashes. The V1 strategies shown above are safe defaults — alphanumeric names pass through as-is for readability, while special characters are hashed to prevent path errors and traversal attacks. Changing sanitization strategies after data has been written is a breaking change, so choose your strategy upfront. + **Characteristics:** - ✅ Data persists across restarts