TechByteByByte

Connecting Agents to MCP

Wire a real, live MCP connection directly into an agent from your LangChain, LangGraph, or LlamaIndex coursework — the moment MCP stops being an isolated protocol and becomes a genuine part of your existing agent architecture.

#MCP#Agents#LangChain#LangGraph

Recall Module 10’s own complete, working client — discovering and calling a real server directly. This module does something genuinely more useful: wiring that same, real connection into an actual agent from your prior coursework, so an LLM decides when to use it, rather than your own code calling it directly.

The real, honest gap this module closes

flowchart LR
    A["Module 10: you called<br/>call_tool() yourself"] --> B["This module: the LLM<br/>decides to call it"]

Recall your LangChain course’s own tool-calling discipline — a real agent reasons over available tools and decides, dynamically, whether to use one. Module 10’s client never did that; you called client.call_tool(...) directly, yourself. This module hands that real decision to an actual LLM.

Turning real MCP tools into real LangChain tools

Let’s connect a genuine, running MCP server directly to a LangChain agent.

We’ll discover the server’s real tools, then wrap each one as a genuine, real LangChain tool the agent can reason over.

from fastmcp import Client
from langchain.tools import StructuredTool
from langchain.chat_models import init_chat_model
from langchain.agents import create_agent

async def build_agent_from_mcp_server(server_path: str):
    client = Client(server_path)
    async with client:
        mcp_tools = await client.list_tools()  # real, live discovery, from Module 10

        def make_langchain_tool(mcp_tool):
            async def call_it(**kwargs):
                async with Client(server_path) as c:
                    result = await c.call_tool(mcp_tool.name, kwargs)  # a real, live MCP call
                    return result.data
            return StructuredTool.from_function(
                coroutine=call_it, name=mcp_tool.name, description=mcp_tool.description,
            )

        langchain_tools = [make_langchain_tool(t) for t in mcp_tools]  # every real, discovered tool, wrapped

    agent = create_agent(model=init_chat_model("openai:gpt-4o-mini"), tools=langchain_tools)
    return agent

Recall Module 5’s own real MCP tool definitions — none of them changed at all. This function genuinely just translates each one into the shape your LangChain course’s own create_agent already knows how to use.

Connecting the same server to a LangGraph node

Recall your LangGraph course’s own real node functions — the exact same, real MCP connection slots in directly.

We’ll build a genuine LangGraph node whose entire job is calling a real, connected MCP tool.

from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from fastmcp import Client

class State(TypedDict):
    order_id: str
    result: str

async def check_order_node(state: State) -> dict:
    async with Client("support_server.py") as client:  # the real server from Module 9
        result = await client.call_tool("get_order_status", {"order_id": state["order_id"]})
        return {"result": result.data}

builder = StateGraph(State)
builder.add_node("check_order", check_order_node)
builder.add_edge(START, "check_order")
builder.add_edge("check_order", END)
graph = builder.compile()

Notice this node is genuinely no different in shape from any node throughout your LangGraph course — recall Module 4’s own real definition, “a node is any callable taking state and returning an update.” It simply happens to call a real MCP server internally, instead of a plain Python function.

Connecting to LlamaIndex’s own real agent tools

Recall your LlamaIndex course’s own FunctionTool — the same real translation applies there too.

from llama_index.core.tools import FunctionTool
from fastmcp import Client

async def create_llamaindex_tool_from_mcp(server_path: str, tool_name: str, description: str):
    async def call_it(**kwargs) -> str:
        async with Client(server_path) as client:
            result = await client.call_tool(tool_name, kwargs)  # a real, live MCP call
            return str(result.data)
    return FunctionTool.from_defaults(async_fn=call_it, name=tool_name, description=description)

The real, deliberate pattern behind all three

Notice this module’s own real, repeated shape, regardless of which framework you’re connecting to.

flowchart TD
    A[Real MCP Server] --> B[Discover real tools]
    B --> C[Wrap each as the framework's native tool type]
    C --> D[Hand to a real agent]
    D --> E[LLM decides, dynamically, whether to call it]

MCP itself never changes — recall Module 1’s own real, deliberate goal, n+m instead of n×m. The real, one-time translation layer is what changes per framework; the actual MCP server, and everything it exposes, stays completely identical.

Common mistakes worth avoiding

Calling client.call_tool(...) directly inside your application logic, then calling this “agentic.” Recall this module’s own opening distinction — genuine agentic use means the LLM decides whether and when to call a tool; your own code calling it directly, even through MCP, is still just Module 10’s client pattern.

Opening a new, real connection on every single tool call, in a genuinely high-traffic application. Recall this module’s own example functions — each real call opens a fresh connection; a production system would genuinely want to reuse one, persistent connection instead, covered fully once this course reaches production architecture.

Assuming the translation layer needs to understand what a tool actually does. Recall this module’s own real functions — they’re genuinely generic, working identically regardless of which specific MCP tool is being wrapped, because they only ever pass through the real name, description, and arguments MCP already provides.

What you should take away from this module

  • MCP tools become real, native tools in any framework through one, genuine, thin translation layer — the actual MCP server never needs to know or care which framework is calling it.
  • A LangGraph node calling an MCP tool is genuinely no different in shape from any other node — it’s still just a callable, taking state and returning an update.
  • The real, structural benefit is precisely Module 1’s own n+m goal — one real MCP server, usable by agents built on LangChain, LangGraph, or LlamaIndex, with no separate integration work for each.

Where this goes next

The next two modules cover practical, direct integration specifically with your own prior courses: MCP with LangChain and LangGraph, and MCP with LlamaIndex — going deeper than this module’s translation layer into genuine, real, idiomatic patterns each framework actually provides.

Author
TechByteByByte Editorial Team
Reviewed by
TechByteByByte Admin
Published
Last reviewed