Connections
import McpLifecycleShortPy from '/snippets/code-samples/mcp-lifecycle-short-py.mdx'; import McpGraphFactoryPy from '/snippets/code-samples/mcp-graph-factory-py.mdx'; import McpSharedPoolPy from '/snippets/code-samples/mcp-shared-pool-py.mdx'; import McpMultiServerPy from '/snippets/code-samples/mcp-multi-server-py.mdx'; import McpClientGroupPy from '/snippets/code-samples/mcp-client-group-py.mdx'; import McpProtocolErasPy from '/snippets/code-samples/mcp-protocol-eras-py.mdx'; import McpLifecycleShortJs from '/snippets/code-samples/mcp-lifecycle-short-js.mdx'; import McpMultiServerJs from '/snippets/code-samples/mcp-multi-server-js.mdx'; import McpSharedAdapterJs from '/snippets/code-samples/mcp-shared-adapter-js.mdx'; import McpProtocolErasJs from '/snippets/code-samples/mcp-protocol-eras-js.mdx';
The
langchain.mcpnamespace requireslangchain[mcp]>=1.4.0and is in beta. The API may change.
@[MCPAdapter] opens an MCP connection, discovers tools, and returns LangChain tools your agent can call. How you pass servers in, and how long you keep the adapter open, depends on the shape of your app. The connection itself is FastMCP's; this page covers the LangChain patterns and links out to FastMCP for transport and client details.
Prefer the default lifecycle unless you are connecting to several servers or deploying many concurrent runs. For what a single target can be (URL, script path, in-process server), see Transports (opens in a new tab).
Choose a pattern
Pick one row from each table. Server shape and connection lifetime are independent; any shape works with any lifetime.
Server shape
| If you need… | Use | Go to |
|---|---|---|
| One server | A URL, Path, or in-process target | Transports (opens in a new tab) |
| Several servers behind one connection | An MCPConfig dict | MCPConfig |
| Several servers with separate auth, protocol eras, or pools | A ClientGroup (opens in a new tab) | ClientGroup |
Connection lifetime
| Situation | Pattern | Go to |
|---|---|---|
| Script, notebook, or most agents | Discover inside async with, then exit | Connection lifecycle |
| Hold one session across several tool calls in a run | Keep the adapter open around the agent call | One session per invocation |
| Many concurrent runs in a deployment | Discover per run; reuse a shared pool and cache | Scale a deployment |
Connection lifecycle
@[MCPAdapter] is an async context manager. Entering it connects the underlying client; exiting it releases the connection. Discovery happens inside the context, but the tools it returns hold the client, so they stay callable after the context exits.
To discover tools and build an agent (the default pattern):
You do not need to keep the adapter open for the life of the agent. Prefer this pattern unless you are holding a session open or scaling a deployment.
One session per invocation
The tools @[MCPAdapter] returns are reentrant: each time a tool is invoked, it opens the client, runs the MCP call, and releases it, whether or not a connection is already held elsewhere. A single agent run therefore opens one session per tool call and closes it when the call returns, rather than holding a session open across the whole run. This keeps a long-running agent from pinning an idle connection between tool calls, and it is why the tools stay callable after the discovery context exits.
If you want a session held open across several calls, keep the adapter's context open when you call the agent. The reentrant client reuses the existing connection rather than opening a second one.
Multiple servers
To give one agent tools from several servers, choose MCPConfig when a single aggregate connection is enough, or ClientGroup when each server needs its own connection (different protocol eras, per-server authentication (opens in a new tab), or a shared pool configured per client).
One aggregate connection with MCPConfig
Give the adapter an MCPConfig dict to connect to several servers behind one aggregate endpoint. FastMCP prefixes every tool with its config key, so two servers exposing the same tool name stay distinguishable in the list handed to a model:
Each backend is addressed independently, so a fleet can mix transports: one server over stdio, another over HTTP. An MCPConfig fleet shares a single negotiated protocol era across every backend, though: add a legacy-only server and the whole fleet drops to the legacy era.
Independent connections with ClientGroup
To keep each server on its own connection, pass a ClientGroup (opens in a new tab). Each member keeps its own negotiated protocol era, authentication, and handlers, and the group routes each call back to the client that advertised the tool. This is what lets a legacy and a modern server run side by side, and it namespaces tools the same way so identical tool names across servers never collide:
Scale a deployment
A deployment that serves many runs should discover per run but reuse its connections underneath, rather than reconnecting on every request. Build the agent inside a langgraph dev (opens in a new tab) graph factory so each run picks up the current tool catalog, and let a shared connection pool and response cache absorb the cost:
In a
langgraph devgraph factory, the annotated parameter types and the return type must be importable at runtime, not only underTYPE_CHECKING.langgraph-apiclassifies the factory withtyping.get_type_hints(); if an annotation cannot resolve, it injects a config dict instead of the runtime.
For a fully worked deployment example, including per-user authentication that mints a token for each caller, see Authentication (opens in a new tab).
Shared connection pool
By default each FastMCP client manages its own HTTP connections. Across a fleet of servers, or many concurrent runs, that means many independent pools. To share one pool, pass an httpx_client_factory that draws from a single transport, and lend it out without letting any one client close it:
Because every client borrows from _POOL, the deployment opens one set of HTTP connections for the whole fleet rather than one per server.
Caching
FastMCP can cache the result of list_tools so repeated discovery avoids 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.
list_tools() accepts a cache_mode that selects how discovery reads a configured cache:
use(the default): serve a cached tool list when one is present and still within the server's TTL hint, otherwise fetch and store.refresh: fetch a fresh list from the server and repopulate the cache.bypass: skip the cache entirely.
tools = await adapter.list_tools(cache_mode="refresh")The cache and its per-principal isolation are configured on the client itself, with Client(cache=...). For a shared store across a fleet of replicas, or partitioning cached responses per user, see Response caching (opens in a new tab) in the FastMCP documentation.
Protocol eras
MCP changed how a client and server agree on what each supports. The legacy era begins every connection with an initialize handshake; the modern era (protocol version 2026-07-28 and later) discovers support by probing the server's server/discover endpoint. FastMCP negotiates the era per connection, so nothing on the LangChain side has to know which a given server speaks.
To hold tools from servers on different eras in one agent, give each its own connection so it keeps the best era its server supports, either through a ClientGroup or one adapter per server. A prebuilt fastmcp.Client selects the era with its mode parameter:
Passing both servers as a single MCPConfig fleet instead would negotiate one era for everything the fleet holds, dropping every server to the oldest era any member requires.
For the full negotiation rules, see Protocol negotiation (opens in a new tab) in the FastMCP documentation.
See also
-
Authentication (opens in a new tab): Bearer, OAuth, and per-user credentials.
-
Deploy a LangGraph server (opens in a new tab): Graph factories for long-lived deployments.