github PrefectHQ/fastmcp v3.3.0
v3.3.0: Slim Reaper

5 hours ago

FastMCP 3.3 ships fastmcp-slim, a new lightweight distribution that separates the client from the server stack. It also closes out a meaningful backlog of security hardening, observability improvements, and auth additions that accumulated through the 3.2 cycle.

fastmcp-slim

The full FastMCP package pulls in Starlette, Uvicorn, and the rest of the server machinery — necessary for running a server, but wasteful if you're writing a client, a script, or an agent that just needs to talk to MCP. fastmcp-slim is a dependency-light distribution that ships the client and transport layer without any of that.

The import namespace is unchanged:

from fastmcp import Client

async with Client("https://example.com/mcp") as client:
    result = await client.call_tool("my_tool", {"arg": "value"})

Install fastmcp-slim[client] anywhere you want FastMCP's client without the server footprint — CI environments, lightweight agents, library dependencies that shouldn't force Uvicorn on downstream users.

Security

The OAuth proxy received three hardening upgrades. Silent consent is now guarded against AS-in-the-middle attacks — a malicious authorization server can no longer silently approve a consent it wasn't meant to handle. Redirect URI allowlist matching now rejects dot-segment paths (/../, /./) that could otherwise bypass prefix checks. And ResponseCachingMiddleware partitions its cache by access token, closing a gap where different users could see each other's cached responses.

Auth

AzureB2CProvider adds first-class support for Azure AD B2C user flows. The OCI provider is fixed for 3.x installs. And OAuthProxy gains a public update_scopes() API for updating the proxy's required scopes after initialization — useful for servers that determine scope requirements at runtime.

Observability

OTEL instrumentation is now fully compliant with MCP semantic conventions. List operations (list_tools, list_resources, list_prompts, list_resource_templates) are instrumented, and delegate spans on proxy servers are enriched with backend attributes.

Thread Affinity

Sync tools run in a thread pool by default. If your tool holds thread-local state or is bound to a specific thread (UI frameworks, some database drivers), you can now opt out:

@mcp.tool(run_in_thread=False)
def my_tool() -> str:
    ...

Under the Hood

Docket is now reentrant, and mounted servers enter their own lifespan — so a server with startup/shutdown logic works correctly when composed into a larger server. The FastMCP constructor accepts experimental_capabilities for passing raw capability flags. Tool errors accept a log_level parameter to control how they're logged. FormInput supports a default prefill value.

Fixes: ping loop now exits cleanly when a stream closes; sampling from background tasks works correctly; Windows startup no longer crashes on non-UTF-8 console output; blank query string values are preserved in OpenAPI routing; $defs introduced by ArgTransform are hoisted to the schema root; HTTP transports are terminated before lifespan shutdown.

13 new contributors this release.

What's Changed

New Features 🎉

  • Add fastmcp-slim for client-only installs by @jlowin in #4122

Enhancements ✨

  • Add default prefill to FormInput.collect_input by @jlowin in #3937
  • OTEL: Fix attribute compliance with MCP semantic conventions by @strawgate in #3889
  • OTEL: Instrument all MCP list operations and enrich delegate spans by @strawgate in #3890
  • Improve real-world schema crash test: failure dump, cluster analysis, TypeErrors baseline ratchet by @jlowin in #3958
  • feat: add AzureB2CProvider for Azure AD B2C user flows by @carlos-rian in #3995
  • Add run_in_thread opt-out for sync tools with thread affinity by @jlowin in #4010
  • Add missing return type annotation to getattr by @ZLeventer in #4026
  • Add experimental_capabilities kwarg to FastMCP constructor by @jlowin in #4042
  • Add log_level parameter to FastMCP errors by @daniel-tsiang in #4036
  • Bump pydocket to 0.20.0 by @chrisguidry in #4031
  • enh: Add public API for updating OAuthProxy scopes after initialization by @taylorwilsdon in #4091
  • Refine fastmcp-slim packaging by @jlowin in #4125

Security 🔒

  • Harden OAuth Proxy silent consent against AS-in-the-middle by @jlowin in #3960
  • Reject dot-segments in redirect URI allowlist matching by @jlowin in #3963
  • Bump deps with open dependabot alerts by @jlowin in #3965
  • Partition ResponseCachingMiddleware cache by access token by @jlowin in #4041

Fixes 🐞

  • fix: reject self-mount to prevent infinite recursion by @strawgate in #3925
  • fix: ProxyTool crashes on non-TextContent error responses by @strawgate in #3926
  • fix: _prune_param and _convert_nullable_field mutate input schemas by @strawgate in #3927
  • fix: narrow OpenAI audio format dict to Literal for ty by @jlowin in #3936
  • fix: allow hyphens in resource template parameter names by @strawgate in #3929
  • fix: OpenAPI request director sends multipart and form-urlencoded as JSON by @strawgate in #3932
  • Fix raise_on_error handling for tool tasks by @gnanirahulnutakki in #3946
  • fix: FileSystemProvider reload race condition by @strawgate in #3938
  • fix tests that relied on task=True returning error results by @jlowin in #3954
  • Restore task snapshot via a worker-level dependency by @chrisguidry in #3945
  • Forward backend capabilities in ProxyProvider by @jlowin in #3956
  • Allow upstream client_id to be used directly without DCR by @jlowin in #3957
  • Graceful fallback for unsupported regex patterns in json_schema_to_type by @jlowin in #3959
  • Revert "Forward backend capabilities in ProxyProvider (#3956)" by @jlowin in #3964
  • fix: skip stdio subprocess test on Windows CI by @jlowin in #3966
  • fix: bound _refresh_locks with LRU eviction to prevent memory leak by @jlowin in #3968
  • fix: handle circular JSON Pointer $ref in dereference_refs by @lawrence3699 in #3896
  • fix: honor upstream refresh token expiry in OAuthProxy by @jlowin in #3990
  • fix: narrow _token_validator with isinstance for ty in AzureProvider.from_b2c by @jlowin in #4007
  • fix: cancel orphaned session_task when Client._disconnect times out by @jlowin in #4011
  • fix: preserve @tool metadata in from_function by @lawrence3699 in #4072
  • fix(openapi): keep blank values in parse_qs (refs #4056) by @MukundaKatta in #4076
  • Fix #4056: keep blank query values, add token bucket regression test by @MukundaKatta in #4069
  • fix(ping): exit ping loop cleanly when session stream is closed by @ashwin153 in #4087
  • Fix sampling from background tasks by @cuyua9 in #4068
  • Make Docket reentrant; mounted servers enter their own lifespan by @jlowin in #4095
  • fix(tool_transform): hoist $defs to schema root when ArgTransform introduces them by @SarthakB11 in #4101
  • fix(auth): silence authlib.jose DeprecationWarning at JWT import by @SarthakB11 in #4100
  • fix: don't cache import map in dev apps bundle by @jlowin in #4106
  • #4084 [Issues] Windows startup crash due to UnicodeDecodeError when l… by @doneman536 in #4092
  • fix: drop exc_info for expected tool failures, remove unreachable ValidationError by @sergeykad in #4029
  • fix: cli option --no-banner is NOT passed to cli but server-spec in-correctly when cli --reload option is specified. by @itaru2622 in #4083
  • Fix None backend_* span attributes on un-renamed proxy components by @ringerc in #4109
  • Fix OCI Provider issue in 3.x version. Add OCI auth provider example … by @kiranthakkar in #4116
  • fix(http): terminate active streamable-HTTP transports before lifespan shutdown by @SarthakB11 in #4118

Docs 📚

Dependencies 📦

  • chore(deps-dev): bump pydantic-monty from 0.0.11 to 0.0.12 by @dependabot[bot] in #3940
  • chore(deps-dev): bump pydantic-monty from 0.0.14 to 0.0.16 by @dependabot[bot] in #3984

Other Changes 🦾

  • fix: Don't completely hide plain mcp.tool app-only tools by @owtaylor in #4112

New Contributors

Full Changelog: v3.2.4...v3.3.0

Don't miss a new fastmcp release

NewReleases is sending notifications on new releases.