From 390a11d7d2307dbe555a86d4f6f54ff049bd55cc Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Thu, 19 Feb 2026 12:05:50 -0500 Subject: [PATCH] Document mounted server state store isolation in upgrade guide (#3236) * Document mounted server state store isolation in upgrade guide * Add missing FastMCP import to upgrade guide example --- .../getting-started/upgrading/from-fastmcp-2.mdx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/getting-started/upgrading/from-fastmcp-2.mdx b/docs/getting-started/upgrading/from-fastmcp-2.mdx index bed6ed7d7..fb8c57bf5 100644 --- a/docs/getting-started/upgrading/from-fastmcp-2.mdx +++ b/docs/getting-started/upgrading/from-fastmcp-2.mdx @@ -56,6 +56,8 @@ BREAKING CHANGES (will crash at import or runtime): 3. ASYNC STATE: ctx.set_state() and ctx.get_state() are now async (must be awaited). State values must be JSON-serializable unless serializable=False is passed. + Each FastMCP instance has its own state store, so serializable state set by parent middleware isn't visible to mounted tools by default. + Fix: pass the same session_state_store to both servers, or use serializable=False (request-scoped state is always shared). 4. PROMPTS: mcp.types.PromptMessage replaced by fastmcp.prompts.Message. Before: PromptMessage(role="user", content=TextContent(type="text", text="Hello")) @@ -222,6 +224,20 @@ State values must also be JSON-serializable by default (dicts, lists, strings, n await ctx.set_state("client", my_http_client, serializable=False) ``` +**Mounted servers have isolated state stores** + +Each `FastMCP` instance has its own state store. In v2 this wasn't noticeable because mounted tools ran in the parent's context, but in v3's provider architecture each server is isolated. Non-serializable state (`serializable=False`) is request-scoped and automatically shared across mount boundaries. For serializable state, pass the same `session_state_store` to both servers: + +```python +from fastmcp import FastMCP +from key_value.aio.stores.memory import MemoryStore + +store = MemoryStore() +parent = FastMCP("Parent", session_state_store=store) +child = FastMCP("Child", session_state_store=store) +parent.mount(child, namespace="child") +``` + **Auth provider environment variables removed** In v2, auth providers like `GitHubProvider` could auto-load configuration from environment variables with a `FASTMCP_SERVER_AUTH_*` prefix. This magic has been removed — pass values explicitly: