Agent tool integration carries a familiar tax: every new API brings its own schema, parameter conventions, and return shape. MCP, the Model Context Protocol, treats that as a shared contract between large language models and external tools. LangChain 1.0 connects through langchain-mcp-adapters, letting MCP tools load into an agent without bespoke glue code.

This walkthrough goes beyond a minimal demo. It covers the protocol model, building synchronous and asynchronous MCP tools, wiring them into LangChain, deploying across local and production setups, and fixing common failure modes.

Why tool standardization matters

Before MCP, agents calling tools ran into three recurring problems:

  • High integration cost: every tool had different API formats, parameter definitions, and response structures, so each new integration needed custom adaptation.
  • Poor cross-platform compatibility: tools were tied to specific models or frameworks and could not be reused across LangChain, LlamaIndex, and other ecosystems.
  • High maintenance burden: when a tool changed, every agent that called it needed synchronized updates.

MCP's central value is standardization. A unified tool description format, communication flow, and data interaction contract turn tools into plug-ins: build once, reuse across platforms.

Three core MCP characteristics

  • Flexible transport: stdio for local subprocesses, Streamable HTTP for remote services, and SSE for real-time streaming communication.
  • Unified tool description: standardized metadata defines tool names, purpose, parameter types, and return formats, and the model can parse it automatically.
  • Broad ecosystem compatibility: works with models that support Function Call and with major agent frameworks such as LangChain, LangGraph, and AutoGPT.

Traditional calls vs. MCP calls

<table> <thead> <tr> <th>Dimension</th> <th>Traditional tool calling</th> <th>MCP standardized calling</th> </tr> </thead> <tbody> <tr> <td>Integration</td> <td>Custom API calls and parameter mapping</td> <td>Follows MCP with a unified integration flow</td> </tr> <tr> <td>Cross-platform reuse</td> <td>Separate adaptation for each framework or model</td> <td>Build once, reuse across platforms</td> </tr> <tr> <td>Maintenance</td> <td>Tool changes require updating all calling code</td> <td>Tools evolve independently; callers stay unchanged</td> </tr> <tr> <td>Extensibility</td> <td>New tools repeat adaptation work</td> <td>New MCP tools plug in without extra adaptation</td> </tr> <tr> <td>Model compatibility</td> <td>Depends on a specific model's Function Call format</td> <td>Compatible with models that support MCP</td> </tr> </tbody> </table>

Environment setup

Install core dependencies

# Install LangChain 1.0 core libraries
pip install langchain==0.1.0 langchain-core==0.1.0
# Install the MCP adapter that bridges LangChain and MCP
pip install langchain-mcp-adapters
# Install the MCP server library for custom tools
pip install mcp
# Install a model client (OpenAI-compatible interface shown here)
pip install openai==1.13.3
# Install async dependencies for HTTP/SSE transport
pip install aiohttp uvicorn

Verify the environment

# Check LangChain version
python -c "import langchain; print(langchain.__version__)"
# Check MCP installation
python -c "import mcp; print('MCP installed successfully')"

The expected result is LangChain 0.1.0 or later and no MCP error, which means the environment is ready.

Configure the model (Qwen example)

Create config.py to keep model settings in one place:

# config.py
api_key = "你的通义千问 API Key"
api_base = "https://dashscope.aliyuncs.com/compatible-mode/v1"
model_name = "Qwen/Qwen3-8B"
model_provider = "openai"  # LangChain compatible with OpenAI interface format
temperature = 0.3  # Lower randomness for more stable tool calls

Building a custom MCP tool from scratch

An MCP tool follows the protocol to define metadata and logic. It can expose synchronous or asynchronous functions and adapt to different transports. The examples below build a math service and a weather service.

Synchronous tool: math service over stdio

This fits lightweight local tools. It communicates through standard input/output and needs no network configuration.

Tool code (math_server.py)

from mcp.server.fastmcp import FastMCP
from pydantic import Field  # Used for parameter validation and description

# Initialize MCP server, service name "MathTools"
mcp = FastMCP("MathTools")

# Define addition tool
@mcp.tool(
    description="计算两个整数的和,适用于简单加法运算",
    # Parameter description (supports Pydantic field features such as required, default, validation rules)
    a=Field(description="第一个整数", ge=-10000, le=10000),  # Numeric range limit
    b=Field(description="第二个整数", ge=-10000, le=10000)
)
def add(a: int, b: int) -> int:
    """返回两个整数的和"""
    return a + b

# Define multiplication tool
@mcp.tool(
    description="计算两个整数的乘积,适用于简单乘法运算",
    a=Field(description="第一个整数", ge=-1000, le=1000),
    b=Field(description="第二个整数", ge=-1000, le=1000)
)
def multiply(a: int, b: int) -> int:
    """返回两个整数的乘积"""
    return a * b

# Define complex calculation tool (multiple parameters, logic handling)
@mcp.tool(
    description="计算表达式:(a + b) × c - d,适用于组合运算",
    a=Field(description="第一个整数"),
    b=Field(description="第二个整数"),
    c=Field(description="第三个整数"),
    d=Field(description="第四个整数", default=0)  # Optional parameter, default value 0
)
def complex_calc(a: int, b: int, c: int, d: int = 0) -> int:
    """按 (a + b) × c - d 计算并返回结果"""
    return (a + b) * c - d

if __name__ == "__main__":
    # Start the service with stdio transport
    mcp.run(transport="stdio")

Key details:

  • Parameter validation uses Pydantic Field to declare descriptions, ranges, and defaults, so invalid input is checked automatically.
  • Tool descriptions help the model understand purpose and choose the right function.
  • Transport flexibility means changing the transport argument switches to HTTP or SSE without changing tool logic.

Asynchronous tool: weather service over Streamable HTTP

Remote tools that serve multiple clients usually use HTTP transport.

Tool code (weather_server.py)

from mcp.server.fastmcp import FastMCP
from pydantic import Field
import asyncio
import aiohttp  # Asynchronous HTTP request library

# Initialize MCP server, service name "WeatherTools"
mcp = FastMCP("WeatherTools")

# Mock weather API configuration (replace with real API in production)
WEATHER_API_URL = "https://api.weatherapi.com/v1/current.json"
WEATHER_API_KEY = "你的天气 API Key"

@mcp.tool(
    description="查询指定城市的实时天气,返回温度、天气状况、湿度信息",
    location=Field(description="城市名称(支持中文/英文,如北京、New York)", min_length=2),
    unit=Field(description="温度单位,可选 celsius(摄氏度)或 fahrenheit(华氏度)", default="celsius")
)
async def get_weather(location: str, unit: str = "celsius") -> str:
    """
    调用天气 API 查询实时天气,返回结构化结果

    Args:
        location: 城市名称
        unit: 温度单位(celsius/fahrenheit)

    Returns:
        格式化的天气信息字符串
    """
    # Asynchronously call the weather API
    async with aiohttp.ClientSession() as session:
        params = {
            "key": WEATHER_API_KEY,
            "q": location,
            "aqi": "no"
        }
        async with session.get(WEATHER_API_URL, params=params) as response:
            if response.status != 200:
                return f"查询失败:城市 {location} 未找到或 API 异常"

            data = await response.json()
            current = data["current"]
            temp = current["temp_c"] if unit == "celsius" else current["temp_f"]
            condition = current["condition"]["text"]
            humidity = current["humidity"]

            # Format the return result
            return (
                f"【{location} 实时天气】\n"
                f"温度:{temp}°{unit.upper()}\n"
                f"天气状况:{condition}\n"
                f"湿度:{humidity}%\n"
                f"更新时间:{current['last_updated']}"
            )

if __name__ == "__main__":
    # Start the service with Streamable HTTP transport, listening on port 8000
    mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)

Start and test

# Start the weather service
python weather_server.py
# Test whether the service is available (run in terminal)
curl http://localhost:8000/mcp/health
# Expected output: {"status": "healthy"}

Integrating MCP tools into a LangChain agent

LangChain 1.0 uses MultiServerMCPClient to manage multiple MCP services, load their tools automatically, and expose them to an agent. Both sync and async calls are supported.

Agent with math and weather tools

import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import Tool
from openai import AsyncOpenAI
from config import api_key, api_base, model_name

# 1. Initialize the model client (async)
async def init_model():
    client = AsyncOpenAI(
        api_key=api_key,
        base_url=api_base
    )
    return client

# 2. Initialize the MCP client to manage multiple tool services
async def init_mcp_client():
    client = MultiServerMCPClient(
        {
            # Local math tool (stdio transport)
            "math": {
                "transport": "stdio",
                "command": "python",
                "args": ["/absolute/path/to/math_server.py"]  # Replace with the actual path
            },
            # Remote weather tool (Streamable HTTP transport)
            "weather": {
                "transport": "streamable_http",
                "url": "http://localhost:8000/mcp"  # Weather service address
            }
        }
    )
    # Load all MCP tools
    tools = await client.get_tools()
    return tools

# 3. Create the agent
async def create_mcp_agent(model_client, tools):
    # Define the prompt template (guide the agent to call tools correctly)
    prompt = ChatPromptTemplate.from_messages([
        ("system", "你是一个智能助手,可使用数学工具和天气工具解决用户问题。"
                   "工具调用规则:"
                   "1. 涉及计算问题,使用 MathTools 中的工具;"
                   "2. 涉及天气查询,使用 WeatherTools 中的工具;"
                   "3. 工具返回结果后,整理成自然语言回答用户。"),
        ("user", "{input}")
    ])

    # Create the agent (new API in LangChain 1.0)
    agent = create_agent(
        llm=model_client,
        tools=tools,
        prompt=prompt,
        agent_type="chat-zero-shot-react-description"  # Zero-shot tool invocation
    )
    return agent

# 4. Test the agent
async def main():
    # Initialize dependencies
    model_client = await init_model()
    tools = await init_mcp_client()
    agent = await create_mcp_agent(model_client, tools)

    # Test math calculation
    math_query = "计算 (3 + 5) × 12 - 8,给出步骤和结果"
    math_response = await agent.ainvoke({
        "input": math_query
    })
    print(f"数学问题:{math_query}")
    print(f"回答:{math_response['output']}\n")

    # Test weather query
    weather_query = "查询上海的实时天气,用摄氏度"
    weather_response = await agent.ainvoke({
        "input": weather_query
    })
    print(f"天气问题:{weather_query}")
    print(f"回答:{weather_response['output']}")

if __name__ == "__main__":
    asyncio.run(main())

What matters in this code:

  • MultiServerMCPClient manages several MCP services at once, including tools with different transports.
  • Automatic loading via client.get_tools() parses MCP metadata and creates LangChain-compatible Tool objects.
  • Prompt guidance sets explicit rules so the model chooses the right tool more reliably.
  • Async support improves concurrency for high-traffic scenarios.

Stateful tool calls

Tools that need context, such as user sessions or stored state, can use client.session() to create a persistent session:

async def test_stateful_tool():
    client = MultiServerMCPClient({
        "math": {
            "transport": "stdio",
            "args": ["/path/to/math_server.py"]
        }
    })

    # Create a persistent session to maintain tool state
    async with client.session("math") as session:
        tools = await session.get_tools()
        agent = await create_mcp_agent(await init_model(), tools)

        # Multiple turns; the tool can maintain context (e.g., accumulated calculation results)
        response1 = await agent.ainvoke({"input": "计算 10 + 20"})
        response2 = await agent.ainvoke({"input": "在上一步结果基础上乘以 3"})

        print(f"第一轮:10 + 20 → {response1['output']}")
        print(f"第二轮:30 × 3 → {response2['output']}")

Deployment choices

MCP supports several transports. Pick one based on tool type, traffic, and environment.

Local development: stdio

  • Use when developing locally, testing a single agent, or calling lightweight tools.
  • Advantages no network setup, simple startup, no extra port.
  • Steps run the tool script and let the agent communicate with the subprocess.

LAN sharing: Streamable HTTP

  • Use when sharing tools inside a team or calling from multiple agents on a LAN.
  • Advantages multiple clients, separate deployment for tools and agents.
  • Steps 1. Start the MCP tool on a server with host=0.0.0.0. 2. Open the firewall port, such as 8000. 3. Call the tool from the agent using server IP and port.

Production: SSE plus load balancing

  • Use when handling high concurrency or real-time streaming responses, such as live data queries.
  • Advantages low latency streaming and support for many concurrent connections.
  • Steps 1. Start the tool service with SSE: mcp.run(transport="sse", port=8000). 2. Put Nginx in front as a load balancer and forward requests to multiple tool instances. 3. Add monitoring and alerts, such as Prometheus plus Grafana.

Containerized deployment with Docker Compose

Containers simplify deployment and version management.

Dockerfile for the weather tool

FROM python:3.10-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

# Copy tool code
COPY weather_server.py .

# Expose port
EXPOSE 8000

# Start the service
CMD ["python", "weather_server.py"]

docker-compose.yml

version: "3"
services:
  math-tool:
    build: ./math-tool
    command: python math_server.py
    volumes:
      - ./math-tool:/app
    restart: always

  weather-tool:
    build: ./weather-tool
    command: python weather_server.py
    ports:
      - "8000:8000"
    environment:
      - WEATHER_API_KEY=你的 API Key
    restart: always

Start

docker-compose up -d

Performance and troubleshooting

Tool-side optimization

  • Validate early inside the tool to reject invalid requests before they consume resources.
  • Prefer async for IO-bound work such as API calls and database queries.
  • Cache hot data for repeated queries, such as weather for popular cities, to avoid duplicate API calls.

Agent-side optimization

  • Filter tools by query semantics instead of passing every tool description and wasting tokens.
  • Batch calls of the same kind to reduce communication overhead.
  • Tune the model with a lower temperature for tool selection, often around 0.3 to 0.5.

Common problems

Tool loading fails

  • Causes wrong path, mismatched transport, or tool service not running.
  • Fix use an absolute path, confirm the transport matches the configuration, and start the service manually to check for port conflicts or missing dependencies.

Tool calls time out

  • Causes network latency, slow tool logic, or too much concurrency.
  • Fix set a timeout such as 5 seconds, optimize the tool with async or caching, and add instances behind a load balancer.

The model calls the wrong tool

  • Causes unclear descriptions, weak prompt guidance, or limited model understanding.
  • Fix sharpen the tool description with the exact use case, add examples and rules to the prompt, or switch to a stronger model such as GPT-4 or Qwen3-8B.

CORS issues with HTTP transport

  • Cause the tool service has no CORS configuration.
  • Fix add CORS middleware to the MCP service:
  from fastapi.middleware.cors import CORSMiddleware

  # Add before mcp.run()
  mcp.app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Restrict to specific domains in production
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
  )

Beyond a single agent

Reuse across frameworks

MCP tools are not limited to LangChain. They can also serve:

  • LangGraph for complex workflow orchestration, with MCP tools as nodes.
  • LlamaIndex through MCPTool as retrieval-augmented tools.
  • AutoGPT by loading tools through an MCP client to extend automated tasks.

More complex tools

  • Multimodal tools can handle image recognition or speech synthesis, with parameters passed as file paths or Base64.
  • Access control can add API key authentication to MCP tools and block unauthorized calls.
  • Logging and monitoring can record request parameters, results, and latency for debugging and analysis.

Building a tool marketplace

MCP also makes a private or public tool marketplace possible:

  • Tool developers publish MCP-compliant tools.
  • Agent developers search and install tools in one step.
  • The marketplace can provide ratings, version management, and update notifications.

MCP plus LangChain 1.0 changes agent tooling from one-off adaptation to standardized integration. Tool builders publish once and reuse across platforms; agent builders focus on business logic; teams get a common interface and lower maintenance cost. The practical path is to start with the protocol, wrap real tools as MCP services, then grow the ecosystem tool by tool.