LangChain and MCP: connecting MCP tools to a LangGraph agent

MCP servers give an agent ready-made tools, and adapters turn them into LangChain tools. The current approach, running on models through an OpenAI-compatible gateway.

5 min · · агенты · development

If you are building an agent on LangChain or LangGraph, sooner or later you will want to plug in ready-made MCP servers: GitHub, databases, the file system, your own utilities. For that you need adapters that turn MCP tools into LangChain tools. Below we cover how to do langchain mcp today, what has changed in the libraries, and how to run such an agent on models through an OpenAI-compatible gateway.

The library information is checked against the langchain-mcp-adapters README and the migration guide in LangChain's documentation on 18 October 2026. Development moves fast, so check class names and versions in the current documentation.

MCP and adapters in brief

MCP (Model Context Protocol) is a protocol where a server declares tools and a client calls them. The agent does not need to know how GitHub or a database works: it sees a list of tools with descriptions and parameters. A LangChain adapter connects to an MCP server, fetches the tool list and wraps each tool so the agent can call it like a regular LangChain tool. What an MCP server is and how it is connected in other programs is described in MCP server: what it is.

What changed: langchain-mcp-adapters and langchain.mcp

This matters if you are reading old examples. The langchain-mcp-adapters repository, per its README, is no longer actively maintained: MCP support has moved into LangChain, into the langchain.mcp namespace. According to the migration guide:

  • new code requires langchain[mcp] version 1.4.0 or newer and is in beta (the import raises a LangChainBetaWarning, and the API may change);
  • instead of MultiServerMCPClient you use the MCPAdapter class, and server configuration is given in the standard mcpServers form;
  • for servers declared in one config, tool names get a {server}_{tool} prefix.

Old projects on langchain-mcp-adapters keep working, but for new code the new option is the sensible choice. Both are below.

Preparation

First, an example MCP server that can add and multiply (taken from the adapters README):

The math_server.py file:

bash
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Math")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b

@mcp.tool()
def multiply(a: int, b: int) -> int:
    """Multiply two numbers"""
    return a * b

if __name__ == "__main__":
    mcp.run(transport="stdio")

Installation for the new option:

bash
pip install "langchain[mcp]" langchain-openai langgraph

Create a fetch_... key in the dashboard and put it in the ZUKKO_API_KEY environment variable.

A LangChain agent with MCP tools

The model is connected as a regular ChatOpenAI with the gateway address https://api.zukko.pro/v1 instead of OpenAI's. You can change the model with one string.

bash
import asyncio
import os

from langchain.agents import create_agent
from langchain.mcp import MCPAdapter
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="gpt-6.1-sol",
    base_url="https://api.zukko.pro/v1",
    api_key=os.environ["ZUKKO_API_KEY"],
)

config = {

The transport is determined by the entry: a server with command and args is a process on your machine (stdio), and an entry with url is an HTTP server. For a single server you can pass the address or script path directly.

The same on langchain-mcp-adapters

If the project already has langchain-mcp-adapters, the code per the README looks like this:

bash
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient(
    {
        "math": {
            "command": "python",
            "args": ["/full/path/math_server.py"],
            "transport": "stdio",
        },
        "weather": {
            "url": "http://localhost:8000/mcp",
            "transport": "http",
        },
    }
)
tools = await client.get_tools()  # inside an async function
agent = create_agent(llm, tools)

The rest (the model, the ainvoke call) does not change. If an MCP tool returns an execution error, by default it reaches the model as a ToolMessage with an error status, and the agent can fix the call on its own.

LangGraph and MCP

If you need your own graph instead of a ready-made agent, the tools are connected the same way and the model is bound with bind_tools. The scheme from the adapters README:

bash
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.prebuilt import ToolNode, tools_condition

model = llm.bind_tools(tools)  # tools obtained via adapter.list_tools() or client.get_tools()

def call_model(state: MessagesState):
    return {"messages": model.invoke(state["messages"])}

builder = StateGraph(MessagesState)
builder.add_node(call_model)
builder.add_node(ToolNode(tools))
builder.add_edge(START, "call_model")
builder.add_conditional_edges("call_model", tools_condition)
builder.add_edge("tools", "call_model")
graph = builder.compile()

Here call_model calls the model, tools_condition decides whether a tool call is needed, and ToolNode executes it. The loop repeats until the model answers with text. For llamaindex mcp the idea is the same: LlamaIndex has its own integration with MCP servers (look for the package in LlamaIndex's documentation) that turns tools into LlamaIndex tools. We did not verify the class names there, so check the documentation. The general approach to LlamaIndex on a gateway: LlamaIndex: connecting.

Which model to take for an agent with tools

The model itself calls the tools, and models handle this differently, so test your choice on your own tasks. By price and strength index (Intelligence Index) as of 18 October 2026 there is something to choose from:

ModelInput / output, $ per 1MContextIndex
claude-sonnet-5-50.4 / 21M48
gpt-6.1-sol0.27 / 1.321M52
deepseek-v4.1-flash0.013 / 0.0381M39

The agent sends the model the whole history on every step, so spend grows with the number of steps. An example: 8 steps, each averaging 6K input tokens and 300 output tokens. In total 48K input and 2.4K output.

  • deepseek-v4.1-flash: 0.048 × 0.013 + 0.0024 × 0.038 = 0.000624 + 0.0000912 ≈ $0.0007;
  • claude-sonnet-5-5: 0.048 × 0.4 + 0.0024 × 2 = 0.0192 + 0.0048 = $0.024.

The cache is billed separately. For Claude the cache discount comes only from the Anthropic format, so with ChatOpenAI all input is billed at the input price. If you need Claude with the cache, use LangChain's Anthropic client with the address https://api.zukko.pro without /v1 (check the name of the address parameter in the package's documentation). For GPT, cache reads are visible in both formats. More: cache reads and writes.

Descriptions of all MCP tools go into the request and count as input tokens. Connect only the servers and tools you need. So the agent does not overspend in a loop, create a key with a daily limit.

Typical problems

  • stdio in a web server. The adapters README warns that stdio was designed for applications on the user's machine. For a server application an HTTP transport is better.
  • Absolute paths. For stdio servers give the full path to the script.
  • 404 model_not_found. Wrong model name. The list is GET /v1/models.
  • 401. Wrong key. 402: not enough balance. 403 model_not_allowed_for_key: the model is not in the key's list.
  • The model does not call a tool. Improve the tool description or try another model.

Access from Russia goes through the gateway, with payment by SBP (the payment page adds 8.5% on top), MasterCard, or crypto.

FAQ

Do I need langchain-mcp-adapters if there is langchain.mcp?

For new code you can use langchain.mcp, but it is in beta. Projects on the old package keep working, though the repository itself is no longer actively maintained.

Which base URL should I use in LangChain?

For ChatOpenAI: https://api.zukko.pro/v1. For the Anthropic client: https://api.zukko.pro without /v1.

Can I use non-OpenAI models in ChatOpenAI?

Yes. The gateway accepts any model in the OpenAI format: change the model string (for example to claude-sonnet-5-5 or deepseek-v4.1-flash).

How do I cap the agent's spend?

Create a separate key and set a daily and monthly limit and a list of allowed models: key limits and cache.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

LangChain MCP: tools in a LangGraph agent · Zukko