Skip to main content
Version: 1.0.5

Multi-Server MCP LangGraph Agent

This guide demonstrates two approaches for building LangGraph agents that connect to multiple MCP servers, then wrap them as uAgents and register them on Agentverse for discovery and use by ASI:One.

If you are new to MCP + LangGraph, start with the simpler single-server guide: LangGraph Agent with MCP adapter.

Roles in this demo:

  • OpenAI gpt-4o is the agent brain (tool selection and reasoning).
  • ASI:One / Agentverse is how users discover and chat with the registered uAgent.
  • Weather tools are mockget_weather returns a fixed sunny reply, not live forecast data.

Overview

Both examples in this guide:

  • Connect to multiple MCP servers (math and weather) using langchain_mcp_adapters.MultiServerMCPClient
  • Use stdio for the local math server and streamable HTTP for the weather server (preferred over deprecated SSE)
  • Wrap the LangGraph agent using uagents_adapter to become a uAgent
  • Register the uAgent on Agentverse, making it discoverable and callable by ASI:One

The key difference is in the agent architecture:

  • Basic Multi-Server Agent: Uses LangGraph's create_react_agent (ReAct auto tool-loop)
  • Advanced State Graph Agent: Uses an explicit StateGraph with call_modelToolNode edges

Transport Methods

In both examples, we use two different transport methods for the MCP servers:

stdio transport (math server)

  • Used for local MCP servers that run as subprocesses
  • Communication happens through standard input/output
  • Good for local development and testing
  • Example: mcp.run(transport="stdio")

HTTP / streamable HTTP transport (weather server)

  • FastMCP serves streamable HTTP with mcp.run(transport="streamable-http") (default path /mcp, port 8000)
  • Clients use transport: "http" and URLs like http://localhost:8000/mcp (langchain-mcp-adapters)
  • SSE (transport: "sse", /sse) is deprecated in current LangChain MCP docs; prefer HTTP unless you intentionally pin an older SSE-first stack

The MultiServerMCPClient handles both transport types. Construct the client plainly, then call tools = await client.get_tools() (do not use async with MultiServerMCPClient(...)).

Common Server Setup

Both examples use the same MCP servers. Set those up first.

1. Create the Math MCP Server

math_server.py
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")

2. Create the Weather MCP Server

weather_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Weather", port=8000)


@mcp.tool()
def get_weather(city: str) -> str:
"""Get the current weather for a city"""
# Mock implementation — not a live weather API
return f"The weather in {city} is sunny and 25°C"


if __name__ == "__main__":
mcp.run(transport="streamable-http")

Prerequisite: Start the weather server before either agent. Multi-server get_tools() can fail or return an incomplete tool set if weather is down.

Approach 1: Basic Multi-Server Agent

This approach uses LangGraph's create_react_agent to create a simple agent that can access tools from multiple MCP servers.

Before you run: Start weather_server.py in another terminal first. Do not run the basic and graph agents at the same time — both bind port 8080.

mcp-multiple-1

Create and Register the Basic Multi-Server Agent

basic_agent.py
import asyncio
import os
import sys
from pathlib import Path

from dotenv import load_dotenv
from langchain_core.messages import HumanMessage
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from uagents_adapter import LangchainRegisterTool, cleanup_uagent
from uagents_adapter.langchain import AgentManager

load_dotenv()

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
API_TOKEN = os.getenv("AGENTVERSE_API_KEY")

model = ChatOpenAI(model="gpt-4o")

AGENT_NAME = "multi_server_agent_math_langchain_mcp"
MATH_SERVER = str(Path(__file__).resolve().parent / "math_server.py")

agent = None
agent_ready = asyncio.Event()


async def setup_multi_server_agent():
global agent

print("Setting up multi-server agent...")
print("Prerequisite: weather_server.py must already be running on port 8000.")
try:
client = MultiServerMCPClient(
{
"math": {
"command": sys.executable,
"args": [MATH_SERVER],
"transport": "stdio",
},
"weather": {
"url": "http://localhost:8000/mcp",
"transport": "http",
},
}
)

tools = await client.get_tools()
print(f"Connected servers: math (stdio), weather (http)")
print(f"Successfully loaded {len(tools)} tools: {[t.name for t in tools]}")

agent = create_react_agent(model, tools)

print("Testing math capabilities...")
math_response = await agent.ainvoke(
{"messages": [HumanMessage(content="what's (3 + 5) x 12?")]}
)
print(f"Math test response: {math_response['messages'][-1].content}")

print("Testing weather capabilities...")
weather_response = await agent.ainvoke(
{"messages": [HumanMessage(content="what's the weather in NYC?")]}
)
print(f"Weather test response: {weather_response['messages'][-1].content}")

agent_ready.set()

while True:
await asyncio.sleep(1)
except Exception as e:
print(f"Error setting up agent: {e}")
print(
"Leaving agent_ready unset so chat requests time out with a clear error "
"instead of soft-failing forever. Fix the setup error and restart."
)
raise


def main():
manager = AgentManager()

async def agent_func(x):
try:
await asyncio.wait_for(agent_ready.wait(), timeout=60)
except asyncio.TimeoutError:
return "Error: Agent setup timed out. Check weather server and logs, then restart."

if agent is None:
return "Error: Agent not initialized properly. Please try again later."

response = await agent.ainvoke({"messages": [HumanMessage(content=x)]})
return response["messages"][-1].content

agent_wrapper = manager.create_agent_wrapper(agent_func)
manager.start_agent(setup_multi_server_agent)

print("Registering multi-server agent...")
tool = LangchainRegisterTool()
agent_info = tool.invoke(
{
"agent_obj": agent_wrapper,
"name": AGENT_NAME,
"port": 8080,
"description": "A multi-service agent that can handle math calculations and weather queries",
"api_token": API_TOKEN,
"mailbox": True,
}
)

print(f"✅ Registered multi-server agent: {agent_info}")

try:
manager.run_forever()
except KeyboardInterrupt:
print("🛑 Shutting down...")
cleanup_uagent(AGENT_NAME)
print("✅ Agent stopped.")


if __name__ == "__main__":
main()

Approach 2: Advanced State Graph Agent

This approach uses LangGraph's StateGraph to create a more sophisticated agent with explicit state management and conditional workflow branching.

Before you run: Start weather_server.py in another terminal first. Do not run this agent at the same time as the basic agent — both use port 8080.

mcp-multiple-2

Create and Register the Multi-Server Graph Agent

graph_agent.py
import asyncio
import os
import sys
from pathlib import Path

from dotenv import load_dotenv
from langchain_core.messages import HumanMessage
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
from langgraph.graph import START, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from uagents_adapter import LangchainRegisterTool, cleanup_uagent
from uagents_adapter.langchain import AgentManager

load_dotenv()

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
API_TOKEN = os.getenv("AGENTVERSE_API_KEY")

model = ChatOpenAI(model="gpt-4o")

AGENT_NAME = "multi_server_graph_agent_math_langchain_mcp"
MATH_SERVER = str(Path(__file__).resolve().parent / "math_server.py")

_global_graph = None
graph_ready = asyncio.Event()


async def setup_multi_server_graph_agent():
global _global_graph

print("Setting up multi-server graph agent...")
print("Prerequisite: weather_server.py must already be running on port 8000.")
try:
client = MultiServerMCPClient(
{
"math": {
"command": sys.executable,
"args": [MATH_SERVER],
"transport": "stdio",
},
"weather": {
"url": "http://localhost:8000/mcp",
"transport": "http",
},
}
)

tools = await client.get_tools()
print(f"Connected servers: math (stdio), weather (http)")
print(f"Successfully loaded {len(tools)} tools: {[t.name for t in tools]}")

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

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")
_global_graph = builder.compile()
print("Graph successfully compiled")

try:
print("Testing math capabilities...")
math_response = await _global_graph.ainvoke(
{"messages": [HumanMessage(content="what's (3 + 5) x 12?")]}
)
print(f"Math test response: {math_response['messages'][-1].content}")

print("Testing weather capabilities...")
weather_response = await _global_graph.ainvoke(
{"messages": [HumanMessage(content="what's the weather in NYC?")]}
)
print(f"Weather test response: {weather_response['messages'][-1].content}")
except Exception as e:
print(f"Error during testing: {e}")

graph_ready.set()

while True:
await asyncio.sleep(1)
except Exception as e:
print(f"Error setting up graph: {e}")
print(
"Leaving graph_ready unset so chat requests time out with a clear error "
"instead of soft-failing forever. Fix the setup error and restart."
)
raise


def main():
print("Initializing agent...")
manager = AgentManager()

async def graph_func(x):
try:
await asyncio.wait_for(graph_ready.wait(), timeout=60)
except asyncio.TimeoutError:
return "Error: Graph setup timed out. Check weather server and logs, then restart."

if _global_graph is None:
error_msg = "Error: Graph not initialized properly. Please try again later."
print(f"Response: {error_msg}")
return error_msg

try:
print(f"\nReceived query: {x}")
messages = [HumanMessage(content=x)] if isinstance(x, str) else x
response = await _global_graph.ainvoke({"messages": messages})
result = response["messages"][-1].content
print(f"\n✅ Response: {result}\n")
return result
except Exception as e:
error_msg = f"Error processing request: {str(e)}"
print(f"\n❌ {error_msg}\n")
return error_msg

agent_wrapper = manager.create_agent_wrapper(graph_func)
manager.start_agent(setup_multi_server_graph_agent)

print("Registering multi-server graph agent...")
tool = LangchainRegisterTool()
try:
agent_info = tool.invoke(
{
"agent_obj": agent_wrapper,
"name": AGENT_NAME,
"port": 8080,
"description": "A multi-service graph agent that can handle math calculations and weather queries",
"api_token": API_TOKEN,
"mailbox": True,
}
)
print(f"✅ Registered multi-server graph agent: {agent_info}")
except Exception as e:
print(f"⚠️ Error registering agent: {e}")
print("Continuing with local agent only...")

try:
manager.run_forever()
except KeyboardInterrupt:
print("🛑 Shutting down...")
cleanup_uagent(AGENT_NAME)
print("✅ Agent stopped.")


if __name__ == "__main__":
main()

Key Differences Between the Two Approaches

In short: the basic agent relies on ReAct’s automatic tool loop via create_react_agent, while the graph agent wires an explicit call_modelToolNode cycle with conditional edges.

  1. Architecture — Basic uses create_react_agent (ReAct-style). Graph uses StateGraph for explicit workflow control.
  2. Control flow — Basic: managed internally by ReAct. Graph: nodes, edges, and tools_condition branching you define.
  3. State management — Basic: implicit inside the ReAct agent. Graph: explicit MessagesState you can inspect and extend.
  4. Extensibility — Basic: quicker setup, less flexible. Graph: more boilerplate, better for multi-step specialized workflows.

Getting Started

  1. Get your Agentverse API key — Follow the Agentverse API Key guide and store the key securely (treat it like a password; rotate it from the Agentverse dashboard if it is exposed).

  2. Set up environment variables in a .env file:

    OPENAI_API_KEY=your_openai_api_key
    AGENTVERSE_API_KEY=your_agentverse_api_key
  3. Install dependencies (include langgraph; pin or match a tested set):

    pip install \
    "langchain-openai>=1.0.0,<2" \
    "langgraph>=0.3.0,<2" \
    "mcp>=1.9.0,<2" \
    "langchain-mcp-adapters>=0.1.0,<0.4" \
    "uagents-adapter>=0.6.2" \
    "python-dotenv>=1.0.0"

    Tested with: Python 3.10–3.12, langgraph 0.3+/1.x (create_react_agent from langgraph.prebuilt), langchain-mcp-adapters 0.1.x–0.3.x (await client.get_tools(), HTTP weather transport), uagents-adapter 0.6.x. Same stack shape as LangGraph Agent with MCP adapter, plus multi-server HTTP.

  4. Create the files — Save math_server.py, weather_server.py, and either basic_agent.py or graph_agent.py in the same directory.

  5. Start the weather server, then one agent (not both agents at once — port 8080 collision):

    # Terminal 1: weather MCP server (required first)
    python weather_server.py

    # Terminal 2: basic agent OR graph agent (pick one)
    python basic_agent.py
    # python graph_agent.py

    Do not run basic_agent.py and graph_agent.py simultaneously; both listen on port 8080.

  6. Test your agent from the Agentverse chat UI.

multi-server-1
  • Click your agent and use Chat with Agent on the Agentverse agent page.
multi-server-2
multi-server-3

Sample success logs

Terminal 1 — weather server up:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Terminal 2 — basic agent (graph agent looks similar):

Setting up multi-server agent...
Prerequisite: weather_server.py must already be running on port 8000.
Connected servers: math (stdio), weather (http)
Successfully loaded 3 tools: ['add', 'multiply', 'get_weather']
Testing math capabilities...
Math test response: (3 + 5) × 12 = 96
Testing weather capabilities...
Weather test response: The weather in NYC is sunny and 25°C
Registering multi-server agent...
✅ Registered multi-server agent: Created uAgent 'multi_server_agent_math_langchain_mcp' ...
INFO: [multi_server_agent_math_langchain_mcp]: Starting mailbox client for https://agentverse.ai

When to Use Each Approach

Use the Basic Agent when:

  • You need a simple agent that can access multiple tools
  • You want a quick setup with minimal boilerplate
  • The agent's decision-making process is relatively straightforward

Use the Graph Agent when:

  • You need more control over the agent's workflow
  • You want explicit state management
  • You need complex conditional branching in your agent's behavior
  • You're building an agent with multiple specialized steps or phases
note

These examples show how to connect to multiple MCP servers with stdio + HTTP transports and two LangGraph architectures. Extend the same patterns to more servers or richer graphs as needed.