fastmcp.Client class provides a programmatic interface for interacting with any MCP server. It handles protocol details and connection management automatically, letting you focus on the operations you want to perform.
Use it to add MCP tools to a Python script, web application, or AI agent. Your application chooses which operations to call; the client discovers and executes them on the server.
Creating a Client
You provide a server source and the client automatically infers the appropriate transport mechanism. This example calls an inventory tool from ordinary Python code and uses its result in the application:async with context manager for proper connection lifecycle management.
Use Cases
Applications and Scripts
Call a known tool withcall_tool() when your application already knows what work to do, such as checking inventory before preparing an order. Use result.data for Python values, or result.content when you need the MCP content blocks, including text, images, and other media. The same client API works with an in-memory server, a local subprocess, or a remote HTTP server; choose the connection in Transports.
AI Agents and Chat Applications
Uselist_tools() to discover each tool’s name, description, and input_schema, then adapt those definitions to your model SDK’s tool format. When the model requests a tool call, pass its name and parsed arguments to call_tool() and return the result to the model in the SDK’s expected format. Your application owns the conversation loop and decides which tools it permits the model to call.
See Calling Tools for discovery, result formats, and error handling. Client Groups let an agent use tools from several independently configured servers through one catalog.
Testing Servers
Pass your server instance toClient(server) in a test to exercise tool discovery and execution without starting a network listener. Assert the returned values just as your application would. The server testing guide shows how to share a client through pytest fixtures.
Discovering and Downloading Skills
An agent can also use the client to retrieve instructions and supporting files from servers that expose skills as resources. The skills client utilities providelist_skills() for discovery, get_skill_manifest() for inspecting files, and download_skill() or sync_skills() for saving them locally.
Choosing a Transport
The client automatically selects a transport based on what you pass to it, but different transports have different characteristics that matter for your use case. In-memory transport connects directly to a FastMCP server instance within the same Python process. Use this for testing and development where you want to eliminate subprocess and network complexity. The server shares your process’s environment and memory space.Configuration-Based Clients
Create clients from MCP configuration dictionaries, which can include multiple servers. While there is no official standard for MCP configuration format, FastMCP follows established conventions used by tools like Claude Desktop.A multi-server configuration presents one aggregate MCP endpoint. Each entry still configures its own transport, headers, and auth, but every backend shares the outer client’s negotiated protocol era, handlers, and client-level policy. To hold several fully independent connections — each with its own protocol
mode and handlers — see Client Groups.Connection Lifecycle
The client uses context managers for connection management. When you enter the context, the client establishes a connection and negotiates the protocol era with the server. Metadata returned by either legacy initialization or modern discovery is exposed through the same client properties.initialize() manually. initialize() is a handshake-era operation, so pin the connection with mode="legacy": the modern protocol has no initialize round trip, and calling it on a modern connection raises.
Protocol negotiation
MCP has two protocol eras: the original legacy era, which begins every connection with aninitialize handshake, and the modern era (protocol version 2026-07-28 and later), which a client discovers by probing the server’s server/discover endpoint. The mode parameter controls which era the client negotiates when it connects.
By default, mode="auto". The client probes server/discover and adopts the modern protocol when the server responds; for any server that is not positive evidence of modern support, it falls back to the legacy handshake. This makes the default safe against a mixed fleet of legacy and modern servers.
mode="legacy" to force the initialize handshake. This behaves identically to earlier FastMCP versions and is the opt-out if a server misbehaves under discovery or you need the legacy initialize result object.
mode="legacy" when you connect to a server that pushes, or when your code calls client.ping() or transport.get_session_id(), which need the session the modern era does not open.
Conversely, background tasks are modern-only: the tasks capability is negotiated over 2026-07-28 connections, so mode="legacy" never triggers one and a task-enabled tool just runs synchronously.
A FastMCP server serves both eras, so a default client negotiates the modern one and the session-dependent calls raise an era-specific error there. Pinning the handshake restores them.
You can also pin a specific modern protocol version to adopt it directly, without a discovery probe:
InitializeResult or modern DiscoverResult, and reset to None when the client disconnects. instructions is also None when the server does not provide any.
When you pin a modern version directly, the client skips discovery and adopts that version with minimal synthesized metadata. In that mode, server_info has an empty name and instructions is None.
mode="auto" is the default as of FastMCP 4.0. Earlier versions defaulted to "legacy". If a server behaves unexpectedly under discovery, or you depend on the legacy initialize result, pin the old behavior with Client(..., mode="legacy").The SSE transport is legacy-only — it cannot carry the sessionless modern era — so a client connecting over SSE always negotiates the legacy handshake, even under mode="auto". A multi-server config (MCPConfigTransport with more than one server) negotiates the best era shared by every connected backend: it stays modern when every backend is modern-capable and reconnects every leg under the handshake era when any backend requires legacy. Pinning mode="legacy" or a modern version applies that mode to every backend. All-modern configurations follow normal modern semantics; applications that rely on handshake-era server-initiated sampling or elicitation should pin mode="legacy". To keep each server on its own natively negotiated era instead, use one client per server — Client Groups coordinate several independent clients under one tool catalog.Response caching
The client can cache the results oflist_tools, list_resources, and list_prompts so that repeated calls avoid a network round-trip. Caching is opt-in and honors the server’s own cache hints, so it only takes effect against modern-era servers that advertise them — a cache is inert on a legacy connection.
Enable the default in-memory cache by passing cache=True. It respects the ttlMs and cacheScope hints the server attaches to each response.
cache=None) and cache=False both disable caching. For control over the store, TTL, or partitioning, pass a CacheConfig. A custom config requires a target_id, since in-memory FastMCP transports expose no server URL to derive a shared-store identity from.
cache_mode: "use" (the default) serves and stores, "refresh" stores a fresh result without serving a cached one, and "bypass" skips the cache entirely. list_tools accepts it directly; the lower-level list_tools_mcp, list_resources_mcp, list_resource_templates_mcp, and list_prompts_mcp variants accept it as well.
Sharing a cache across clients
The default cache lives in each client’s process. To share cached responses across a fleet — a set of proxy replicas backed by one Redis, for example — pass aKeyValueResponseCacheStore, FastMCP’s adapter over the same AsyncKeyValue key-value abstraction the event store and OAuth proxy use. It accepts any compatible backend (memory, Redis, and more).
A shared store mingles responses from different principals, so it requires an explicit partition that isolates them. Derive the partition from a verified credential — never from request data or the server URL — and construct a new client when the principal changes. Only responses the server marks "public" are ever served across partitions.
clear() affects only that namespace, never another tenant’s entries.
Client extensions
Client extensions (SEP-2133) are the advanced mechanism a client uses to opt into vendor capabilities that live outside the core protocol. An extension is aClientExtension instance that bundles three things: a capability advertisement the server can read, one or more result claims that let the client parse extra tools/call result shapes, and notification bindings that observe server notifications the core protocol doesn’t define. Pass a sequence of them to extensions=.
client.call_tool() resolves it transparently through the owning claim’s resolver and hands you back an ordinary result. Result claims and their advertisements are honored only on modern-era connections, so they are inert on a legacy handshake.
For the rare case where you need to register additional result claims against an extension that is already advertised, pass them through result_claims=, keyed by the extension’s identifier. Prefer declaring claims on the extension itself; this parameter merges extra claims with an extension’s own.
Operations
FastMCP clients interact with three types of server components. Tools are server-side functions that the client can execute with arguments. Call them withcall_tool() and receive structured results.
read_resource() using URIs.
get_prompt().
Callback Handlers
The client supports callback handlers for advanced server interactions. These let you respond to server-initiated requests and receive notifications. Sampling, elicitation, and roots are the requests a server makes of the client. A server reaches your handler by whichever route its era allows — pushed down the open session on the handshake, returned as an input-required result on the modern protocol — and both routes dispatch to the same handler, so one registration covers both. Logging and progress arrive as notifications on the response stream and work in either era.- Sampling - Respond to server LLM requests
- Elicitation - Handle server requests for user input
- Progress - Monitor long-running operations
- Logging - Handle server log messages
- Roots - Provide local context to servers

