MCP Integration with LangGraph

Connect LangGraph agents to external MCP servers — build an Airbnb search agent using langchain-mcp-adapters and the @openbnb/mcp-server-airbnb package.

Jun 15, 20269 min readFollow

Topics You Will Master

Understanding the Model Context Protocol (MCP) and how it standardizes tool integration for LLM agents
Installing prerequisites: Node.js (for npx), langchain-mcp-adapters, and the Airbnb MCP server
Configuring MultiServerMCPClient to connect to MCP servers via stdio transport
Loading MCP tools asynchronously with await client.get_tools()

The Model Context Protocol (MCP) is an open standard that lets LLM agents discover and use tools exposed by external servers. Instead of writing custom API wrappers for every service, you connect to an MCP server that exposes a standardized tool interface — the agent discovers available tools, their parameters, and calls them through a unified protocol.

This lesson uses langchain-mcp-adapters to connect a LangGraph agent to the @openbnb/mcp-server-airbnb MCP server. The server exposes two tools for searching and inspecting Airbnb listings — the agent discovers them automatically at startup.

Diagram showing the agent connecting to an external MCP server via stdio and discovering tools at runtime

Prerequisites: Node.js (v18+) for npx, langgraph, langchain-ollama, langchain-mcp-adapters, python-dotenv installed. Ollama running with qwen3.

BASH
# Install Node.js from https://nodejs.org/
node --version  # Should show v18+

# Install Python dependencies
pip install -U langchain-mcp-adapters langgraph langchain-ollama python-dotenv

# Pull the model
ollama pull qwen3

Note

The Airbnb MCP server is installed automatically via npx on first run — no manual installation needed. npx downloads @openbnb/mcp-server-airbnb from npm and runs it as a subprocess.

MCP Mastery — Build AI Apps with Claude and LangChain

Build MCP servers and clients with Python, Streamlit, ChromaDB, LangChain, LangGraph agents, and Ollama integrations.

Enroll on Udemy →

MCP Server Setup

Configuration

The MultiServerMCPClient connects to one or more MCP servers. Each server is defined by a command, arguments, and transport type. The Airbnb server uses stdio transport — it runs as a subprocess and communicates via stdin/stdout:

PYTHON
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "airbnb": {
        "command": "npx",
        "args": ["-y", "@openbnb/mcp-server-airbnb", "--ignore-robots-txt"],
        "transport": "stdio",
    }
})
  • command: npx — Node.js package runner
  • args: ["-y", "@openbnb/mcp-server-airbnb", "--ignore-robots-txt"] — auto-install the package and ignore robots.txt for testing
  • transport: "stdio" — communicate via standard input/output

Available Tools

The Airbnb MCP server exposes two tools:

Diagram showing the MCP server exposing two tools for searching and inspecting Airbnb listings

Tool Description Required Parameters
airbnb_search Search Airbnb listings with filters location
airbnb_listing_details Get detailed property information id (listing ID)

airbnb_search parameters:

  • location (required): City or area to search (e.g. "San Francisco, CA")
  • checkin / checkout: Dates in YYYY-MM-DD format
  • adults, children, infants, pets: Guest counts
  • minPrice / maxPrice: Price range filters
  • cursor: Pagination token

airbnb_listing_details parameters:

  • id (required): Airbnb listing ID
  • checkin, checkout, adults, etc. (optional)

The airbnb_mcp.py Module

The complete MCP agent is implemented as a standalone Python module. It uses async functions throughout because MCP tool loading and execution are asynchronous:

PYTHON
"""Simple Airbnb MCP module."""

from langchain_ollama import ChatOllama
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_core.messages import HumanMessage

from typing_extensions import TypedDict, Annotated
import operator

# Config
LLM_MODEL = "qwen3"
BASE_URL = "http://localhost:11434"

llm = ChatOllama(model=LLM_MODEL, base_url=BASE_URL, temperature=0)


class AgentState(TypedDict):
    messages: Annotated[list, operator.add]

Async Tool Loading

PYTHON
from langchain_mcp_adapters.client import MultiServerMCPClient
import asyncio


async def get_tools():

    client = MultiServerMCPClient(
        {
            "airbnb": {
                "command": "npx",
                "args": ["-y", "@openbnb/mcp-server-airbnb", "--ignore-robots-txt"],
                "transport": "stdio"
            }
        }
    )


    tools = await client.get_tools()

    print(f"Loaded {len(tools)} Tools")
    print(f"Tools Available: {tools}")

    return tools

get_tools() connects to the MCP server, discovers all available tools, and returns them as LangChain StructuredTool objects. These are fully compatible with llm.bind_tools() and ToolNode().

Diagram showing the client spawning the MCP server, discovering tools, and binding them to the LLM

Async Agent Node

PYTHON
async def agent_node(state: AgentState):
    tools = await get_tools()

    llm_with_tools = llm.bind_tools(tools)

    response = llm_with_tools.invoke(state['messages'])

    return {'messages': [response]}

Async Graph Creation

PYTHON
async def create_agent():
    tools = await get_tools()

    builder = StateGraph(AgentState)
    builder.add_node('agent', agent_node)
    builder.add_node('tools', ToolNode(tools))


    # add edges
    builder.add_edge(START, 'agent')
    builder.add_edge('tools', 'agent')

    builder.add_conditional_edges('agent', tools_condition)

    graph = builder.compile()

    return graph

tools_condition from langgraph.prebuilt automatically routes to tools if the agent's response contains tool calls, or to END if it does not — replacing the manual should_continue router used in previous lessons.

Search Function

PYTHON
async def search(query):
    agent = await create_agent()
    result = await agent.ainvoke({'messages': [HumanMessage(query)]})

    response = result['messages'][-1].content

    print(response)

    return response


if __name__=="__main__":
    query = "Show me premium hotels for party in Mumbai"
    asyncio.run(search(query))

Running from a Notebook

Jupyter notebooks run their own event loop, which conflicts with asyncio.run(). The workaround is to run the MCP agent as a subprocess:

PYTHON
import subprocess
import sys

def ask_airbnb(query):

    code = f"""
import asyncio
from airbnb_mcp import search
asyncio.run(search("{query}"))
"""
    
    result = subprocess.run([sys.executable, '-c', code], capture_output=True, text=True)

    return result.stdout

End-to-End Demo

PYTHON
query = "show me the best hotels in mumbai"
response = ask_airbnb(query)

print(response)
OUTPUT
Loaded 2 Tools
Tools Available: [StructuredTool(name='airbnb_search', ...), StructuredTool(name='airbnb_listing_details', ...)]

The agent loaded the two MCP tools, called airbnb_search with location="mumbai", and returned formatted listing results with names, prices, ratings, and direct Airbnb links.


How MCP Integration Works

Diagram showing the LangGraph agent loop where tools_condition routes to the MCP ToolNode or to END

The key advantage of MCP: the agent discovers tools at runtime rather than having them hardcoded. If the MCP server adds new tools or changes parameters, the agent adapts automatically on the next get_tools() call.


Troubleshooting

npx not found:

BASH
# Install Node.js from https://nodejs.org/
node --version  # Should show v18+

On Linux/macOS: Install Node.js via your package manager (brew install node or sudo apt install nodejs npm).

Connection timeout:

  • Check your internet connection — npx downloads the MCP server package on first run
  • Airbnb may be rate-limiting requests — wait and retry

Tool not found:

  • Ensure npx installed @openbnb/mcp-server-airbnb successfully
  • Check terminal for error messages during the npx execution

What You Built

In this lesson you connected a LangGraph agent to an external MCP server:

  • MCP protocol — standardized tool discovery and execution via langchain-mcp-adapters
  • MultiServerMCPClient — configures server connections with command, args, and transport settings
  • Async architectureasync def agent_node, async def create_agent, await client.get_tools() for non-blocking MCP communication
  • tools_condition — automatic routing based on tool calls, replacing manual router functions
  • Airbnb toolsairbnb_search (search listings by location, dates, guests, price) and airbnb_listing_details (detailed property info)
  • Subprocess wrappersubprocess.run() workaround for running async MCP agents from Jupyter notebooks

MCP decouples tool implementation from agent logic — any MCP-compatible server (databases, APIs, file systems) can be connected to the agent by adding a single entry to the MultiServerMCPClient configuration.

Found this useful? Keep building with me.

New tutorials every week on YouTube — or go deeper with a full structured course.

Find this tutorial useful?

Subscribe to our YouTube channels for more practical production walk-throughs.

Discussion & Comments