
This tutorial is part of Build an MCP Server, within The MCP Guide. Want it built for you? See MCP development.
If you search for a langchain-mcp-adapters tutorial in November 2026, most results will teach you a package its own maintainers have stopped working on. The langchain-mcp-adapters README now opens with "This repository is no longer actively maintained" and tells you to "migrate to langchain[mcp]."
This guide does two jobs. It shows you how to load MCP tools into a LangChain agent with the new langchain.mcp namespace and its MCPAdapter class. Then it maps every common langchain-mcp-adapters call to its replacement, so you can move old code across. Code was checked against the LangChain MCP docs and langchain 1.4.8 on PyPI on 2 November 2026.
Key Takeaways
- MCP support now ships inside LangChain as
langchain.mcp; install it withpip install "langchain[mcp]"(version 1.4.0 or later). MultiServerMCPClientis replaced by one class,MCPAdapter, used as an async context manager withlist_tools().MCPAdapteris built on FastMCP and infers the transport from what you pass it: a URL, a script path, an in-process server or anmcpServersconfig.- The namespace is in beta and raises a
LangChainBetaWarning; prompts and resources are not wrapped yet. - A string target must be an
httporhttpsURL; LangChain rejects other strings so a model cannot trick it into launching a local file.
What changed and why it matters
The old package gave you MultiServerMCPClient, load_mcp_tools and typed connection classes. The migration guide says MCP support "now ships inside LangChain in the langchain.mcp namespace, built on FastMCP," and that MultiServerMCPClient "is collapsed into a single MCPAdapter class."
Two practical effects follow. Your dependency becomes langchain[mcp], which pulls in FastMCP. On PyPI that extra required fastmcp>=4.0.11 on 2 November 2026. And the transport is now inferred, so most per-server transport keys disappear from your config.
The LangChain docs flag that the namespace "requires langchain[mcp]>=1.4.0 and is in beta." Importing it raises a LangChainBetaWarning once per process, and "the API may change." Pin your version.
For the bigger picture of when to use MCP tools versus native LangChain tools, see MCP vs LangChain tools.
Prerequisites
- Python 3.10 or later (the
langchainpackage on PyPI requires it). - An API key for the chat model you plan to use.
- Node.js, if you want to test with the MCP Inspector.
Install
pip install "langchain[mcp]"
Or with uv:
uv add "langchain[mcp]"
If an old project has the standalone package, remove it in the same step:
pip uninstall langchain-mcp-adapters
Build a small test server
You need an MCP server to talk to. Because langchain[mcp] already installs FastMCP, a tiny FastMCP server is the quickest option. This follows the FastMCP quickstart:
# notes_server.py
from fastmcp import FastMCP
mcp = FastMCP("Notes")
NOTES = {"welcome": "Hello from the notes server."}
@mcp.tool
def list_notes() -> list[str]:
"""List the titles of all saved notes."""
return sorted(NOTES)
@mcp.tool
def read_note(title: str) -> str:
"""Read one note by its exact title."""
return NOTES.get(title, f"No note titled {title!r}.")
if __name__ == "__main__":
mcp.run(transport="http", port=8000)
Run python notes_server.py. FastMCP serves it at http://localhost:8000/mcp. If you want to go deeper on FastMCP itself, read our FastMCP tutorial.
Minimal working example
This is the quickstart pattern from the LangChain docs, pointed at the test server:
import asyncio
from langchain.agents import create_agent
from langchain.mcp import MCPAdapter
async def main():
async with MCPAdapter("http://localhost:8000/mcp") as adapter:
tools = await adapter.list_tools()
agent = create_agent("claude-sonnet-5", tools)
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "What does the welcome note say?"}]}
)
print(result["messages"][-1].content)
asyncio.run(main())
list_tools() discovers the server's tools and adapts each one into a normal LangChain tool. The docs note that "the tools hold the client, so the agent stays usable after the context exits." Swap the model string for any model your LangChain setup supports.
Transports: what you pass decides how it connects
The MCP overview page lists the targets MCPAdapter accepts:
| Target | Transport |
|---|---|
"https://example.com/mcp" (a string URL) | Streamable HTTP |
Path("weather_server.py") | stdio, one subprocess per adapter |
| A FastMCP server object | In memory, no subprocess or socket |
{"mcpServers": {...}} | Several servers behind one adapter |
A prebuilt fastmcp.Client | Whatever you configured on it |
The in-memory option is the best way to unit-test tools: MCPAdapter(mcp) with the mcp object from notes_server.py runs without a port.
One safety rule is worth knowing. The docs warn that FastMCP tests a string "as a filesystem path before testing it as a URL," so a string naming a .py file could launch it. MCPAdapter therefore "rejects strings that do not match the shape of a URL." Use Path(...) for local scripts.
Connect several servers
Use the standard mcpServers shape. The migration guide's example:
from langchain.mcp import MCPAdapter
config = {
"mcpServers": {
"math": {"command": "python", "args": ["/path/to/math_server.py"]},
"weather": {"url": "http://localhost:8000/mcp"},
}
}
async with MCPAdapter(config) as adapter:
tools = await adapter.list_tools()
For a multi-server config, prefixing is automatic and tool names become {server}_{tool}. That stops two servers that both have a search tool from colliding.
Migration cheat sheet
From the migration guide:
Old (langchain-mcp-adapters) | New (langchain.mcp) |
|---|---|
from langchain_mcp_adapters.client import MultiServerMCPClient | from langchain.mcp import MCPAdapter |
from langchain_mcp_adapters.tools import load_mcp_tools | MCPAdapter(...).list_tools() |
convert_mcp_tool_to_langchain_tool | as_langchain_tool (renamed) |
MCPToolArtifact | from langchain.mcp import MCPToolArtifact |
client.get_tools() | await adapter.list_tools() |
"transport": "stdio" / "http" keys | Removed; inferred from command or url |
tool_name_prefix | Automatic for multi-server configs |
handle_tool_errors | Removed as a flag (see below) |
tool_interceptors | LangChain @wrap_tool_call middleware |
Per-connection headers | StreamableHttpTransport(url, headers=...) |
Before you migrate, search the codebase for these. The guide marks them as gaps to stop and review:
get_promptandget_resources: "Not supported."WebsocketConnection: no FastMCP transport; move the server to Streamable HTTP.SSEConnection: still possible throughClient(SSETransport(url)), but the transport is deprecated.to_fastmcp: listed as a feature to review, with no named replacement.
How errors behave now
The Tools page says a server result with isError=True becomes a ToolMessage with status="error", so the agent can read the message and try again. Transport or session failures still raise. You no longer choose this with a flag.
Structured results land on the ToolMessage as an MCPToolArtifact, and image or file content is converted to LangChain content blocks.
Add a tool
Adding a tool happens on the server, not in LangChain. Add another decorated function to notes_server.py, restart it, and the next list_tools() call picks it up:
@mcp.tool
def count_notes() -> int:
"""Return how many notes are saved."""
return len(NOTES)
Write the docstring for the model. It becomes the tool description the agent reads when it decides what to call.
Test with the MCP Inspector
Check the server on its own before you blame the agent:
npx @modelcontextprotocol/inspector --cli http://localhost:8000/mcp --transport http --method tools/list
If the tools appear here but not in LangChain, the problem is on the client side. Our MCP Inspector lesson shows how to call tools and script these checks in CI.
Deploy notes
- Run remote servers over Streamable HTTP. stdio targets start a subprocess per adapter, which suits local tools and tests, not shared services.
- Keep tokens in headers or a
fastmcp.Clientwithauth=, not in URLs. - Read tool annotations defensively. Each adapted tool may carry MCP hints such as
destructive_hintundertool.metadata["mcp"], but every field is optional. Our tool annotations guide explains why hints are advisory.
Pitfalls
- Copying old blog code.
from langchain_mcp_adapters...imports still work only if the retired package is installed. New projects should not add it. - Stale server examples. The old README's server uses
from mcp.server.fastmcp import FastMCP. The official Python SDK v2 removed that path; usefrom fastmcp import FastMCPor the SDK'sMCPServer. - Passing a bare string path. Use
Path(...). Strings must be URLs. - Expecting prompts and resources. The adapter wraps tools only, for now.
Next step
Work through the free MCP course to understand what the adapter hides. If you need a production MCP server behind your LangChain agents, our MCP development team can build and secure it.
More in the MCP Guide
Free tools: server.json Validator · MCP Error Code Lookup
Latest research: State of MCP: October 2026
Frequently asked questions
Is langchain-mcp-adapters deprecated?
Its README says it is "no longer actively maintained" and points to langchain[mcp]. LangChain publishes a step-by-step migration guide from MultiServerMCPClient to MCPAdapter.
Does MCPAdapter work with LangGraph?
Yes. list_tools() returns standard LangChain tools, and LangGraph graphs use LangChain tools. Our LangGraph MCP tutorial shows the graph wiring.
Can I still use SSE servers?
The migration guide says SSE works through Client(SSETransport(url)) passed to MCPAdapter, but the transport is deprecated. Ask the server owner for a Streamable HTTP endpoint.
Why do my tool names now have a prefix?
A multi-server mcpServers config prefixes names automatically as {server}_{tool}. Point an adapter at a single server if you want the original names.











