使用说明
Math-Weather-MCP-Integration
A small reference build showing one LangChain agent talking to two MCP servers at once — one over stdio, one over HTTP.
Overview
This is a reference implementation of the Model Context Protocol (MCP) with more than one server in play. The point I wanted to prove to myself: a single agent can hold tools from two MCP servers that speak different transports and pick the right one on its own, with no manual switching.
There are two servers here. A Math server runs as a local subprocess and talks over stdio. A Weather server runs as a network service and talks over streamable HTTP. The client wires both into one MultiServerMCPClient, hands the combined tool set to a Groq-hosted Llama 3.3 70B agent, and lets the model route each query — math goes to the math tool, weather goes to the weather tool.
It's deliberately small (around 1.7 KB of Python across three files). The weather tool returns a canned string rather than calling a real API, so treat this as a working skeleton for multi-server, multi-transport MCP agents rather than a finished product. It was built as a learning/demo project alongside a set of other MCP experiments.
Key Features
- Two MCP servers, two transports, one agent. Math over
stdio, weather overstreamable-http, both exposed to the same client. MultiServerMCPClientaggregation. Tools from both servers are collected throughlangchain-mcp-adaptersand merged into a single tool list the agent can call.- LLM-driven tool routing. A LangChain agent backed by
ChatGroq(Llama 3.3 70B) reads the natural-language question and decides which tool to invoke — no hardcodedif/elsedispatch. - Auto-spawned subprocess server. The math server is launched by the client itself (
command: python,args: ["mathserver.py"]), so you don't start it by hand. FastMCPtool definitions. Both servers use theFastMCPhelper from themcpSDK; tools are plain Python functions decorated with@mcp.tool(), and their docstrings become the tool descriptions the model sees.- Async end to end. The client is built on
asyncio— tool discovery (await client.get_tools()) and agent calls (await agent.ainvoke(...)) are all non-blocking. - Two worked examples baked in. On run it fires a math query (
(3 + 5) x 12) and a weather query (weather in California) and prints both answers, so you can confirm the wiring in one shot.
How It Works
The project is three files: a client and two servers. The agent never knows or cares which transport a tool lives behind — that's the whole idea.
Math server (mathserver.py) — stdio
A FastMCP("Math") instance exposes one tool:
@mcp.tool()
def calculate(expr: str) -> float:
"""Evaluate math expression like '(3+5)*12'."""
return eval(expr)
It runs with mcp.run(transport="stdio"), so it communicates over standard input/output as a child process. The docstring (Evaluate math expression like '(3+5)*12') is what the LLM reads to decide when to call it. Note: it evaluates the expression with Python's built-in eval, which is fine for a trusted local demo but is the obvious thing to replace with a safe parser before this goes anywhere real.
Weather server (weather.py) — streamable HTTP
A second FastMCP("Weather") instance exposes an async tool:
@mcp.tool()
async def get_weather(location: str) -> str:
"""Get the weather location."""
return "It's always raining in California"
It runs with mcp.run(transport="streamable-http") and is meant to be reachable at http://localhost:8000/mcp. The return value is a fixed string — this is a placeholder standing in for a real weather API call, which keeps the demo deterministic and dependency-free.
Client (client.py) — the agent
The client is where the two transports come together:
client = MultiServerMCPClient({
"math": {"command": "python", "args": ["mathserver.py"], "transport": "stdio"},
"weather": {"url": "http://localhost:8000/mcp", "transport": "streamable_http"},
})
From there the flow is:
- Discover tools —
tools = await client.get_tools()connects to both servers (spawning the math subprocess, opening the HTTP session) and returns their tools as LangChain-compatible tools. - Build the agent —
model = ChatGroq(model="llama-3.3-70b-versatile")andagent = create_agent(model, tools). The Groq API key is read from the environment viapython-dotenv. - Invoke — each user message goes to
agent.ainvoke({"messages": [...]}). The model inspects the available tools, calls the relevant one, and the tool result is folded back into the answer. - Print — the final message content for both the math and weather queries is printed to the console.
The agent decides routing entirely from the tools' names and docstrings — a math question lands on calculate, a weather question lands on get_weather.
Tech Stack
- Language: Python 3.13
- Agent framework: LangChain (
create_agent), LangGraph - MCP:
mcpSDK /FastMCP,langchain-mcp-adapters(MultiServerMCPClient) - LLM: Groq —
langchain-groqChatGroq, modelllama-3.3-70b-versatile - Transports: stdio (subprocess) and streamable HTTP
- Runtime / tooling:
asyncio,python-dotenv,uv(withuv.lockchecked in) or plainpip
Getting Started
Prerequisites
- Python 3.13 (pinned in
.python-version) - A Groq API key (free tier works) — get one from the Groq console
uvis optional;pipis fine
Installation
git clone https://github.com/DCode-v05/Math-Weather-MCP-Integration.git
cd Math-Weather-MCP-Integration
# with uv (uses the checked-in uv.lock)
uv sync
# or with pip
pip install -r requirements.txt
Configure
Create a .env file in the project root (it's already in .gitignore):
GROQ_API_KEY=your_groq_api_key_here
Running
The weather server runs over HTTP, so start it first in its own terminal:
python weather.py # serves MCP at http://localhost:8000/mcp
Then run the client in a second terminal:
python client.py
You don't need to start the math server yourself — client.py spawns mathserver.py as a subprocess over stdio.
Expected output is two lines, one per built-in query:
Math response: 96
Weather response: It's always raining in California
Usage
The two queries are hardcoded in main() inside client.py. To try your own, edit the content of the messages passed to agent.ainvoke, for example:
resp = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what is 144 / 12 + 7?"}]}
)
print(resp["messages"][-1].content)
Anything math-shaped routes to the calculate tool; anything weather-shaped routes to get_weather. To make the weather answer real, swap the placeholder return in weather.py for an actual API call (e.g. Open-Meteo or OpenWeather) keyed on location.
Project Structure
Math-Weather-MCP-Integration/
├── client.py # Agent + MultiServerMCPClient; wires math (stdio) + weather (http), routes via Groq Llama 3.3
├── mathserver.py # FastMCP "Math" server, calculate() tool, stdio transport
├── weather.py # FastMCP "Weather" server, get_weather() tool, streamable-http transport
├── pyproject.toml # Project metadata + dependencies (langchain, langgraph, mcp, langchain-groq, adapters)
├── requirements.txt # pip dependency list
├── uv.lock # Locked dependency versions for uv
├── .python-version # Pins Python 3.13
├── .gitignore # Ignores .env
└── README.md
Contact
| Portfolio: Denistan | LinkedIn: denistanb |
| GitHub: DCode-v05 | LeetCode: Denistan_B |
| Email: denistanb05@gmail.com |
Made with ❤️ by Denistan B
