MCP Client
In shortConnect to MCP servers, discover remote tools, and invoke them directly using FastMCP.
- 3 min read
- 9 sections
- Updated
- v0.10.0
- Markdown
What the example shows
The FastMCP Client connects to an MCP server, lists its tools with their schemas, and calls one directly, with no graph involved. Use this pattern to inspect a server or call tools from a script, or to learn MCP before wiring it into a 10xGraph agent.
The example pairs a small weather server with a client that lists the tools and calls get_weather.
How to run it
The files are in agentflow/examples/react-mcp/. Install the packages (the client also imports python-dotenv), then start the server in one terminal.
pip install fastmcp python-dotenv
python agentflow/examples/react-mcp/server.pyThe server uses the streamable HTTP transport and serves the MCP endpoint at http://127.0.0.1:8000/mcp. In a second terminal, run the client.
python agentflow/examples/react-mcp/client.pyYou see the tool name and tags, the full tool definition, then the result of calling get_weather for New York.
The server exposes one tool
The server registers get_weather with a description and tags, then runs over streamable HTTP. The tags are what the client later reads from the tool metadata.
from fastmcp import FastMCP
mcp = FastMCP("My MCP Server")
@mcp.tool(
description="Get the weather for a specific location",
tags={"weather", "information"},
exclude_args=["user_details"],
)
def get_weather(location: str, user: dict | None = None) -> dict:
print(f"User Details: {user}")
return {
"location": location,
"temperature": "22°C",
"description": "Sunny",
}
if __name__ == "__main__":
mcp.run(transport="streamable-http")The client lists and calls the tool
The client declares its servers in a config dict, opens the connection with an async context manager, lists the tools, and calls one by name. The same file is shown whole so you can run it as is.
import asyncio
from dotenv import load_dotenv
from fastmcp import Client
from mcp import Tool
load_dotenv()
# Map server names to connection details.
config = {
"mcpServers": {
"weather": {
"url": "http://127.0.0.1:8000/mcp",
"transport": "streamable-http",
"headers": {"Authorization": "Bearer TEST_WEATHER_API_KEY"},
},
},
}
client_http = Client(config)
async def call_tools():
# Discover tools and print their tags and full definitions.
async with client_http:
tools: list[Tool] = await client_http.list_tools()
for i in tools:
meta = i.meta or {}
tags = meta.get("_fastmcp", {}).get("tags", [])
print(f"Tool: {i.name}, Tags: {tags}")
print(i.model_dump())
async def invoke():
# Call a tool by name with a dict of arguments.
async with client_http:
result = await client_http.call_tool(
"get_weather",
{
"location": "New York",
},
)
print(result)
async def main():
await call_tools()
await invoke()
if __name__ == "__main__":
asyncio.run(main())The first line of output looks like this (example output, the tool definition that follows varies with the FastMCP version):
Tool: get_weather, Tags: ['weather', 'information']The call prints a CallToolResult that holds the content, the parsed structured_content and data, and an is_error flag. For this tool, data is {'location': 'New York', 'temperature': '22°C', 'description': 'Sunny'}.
What each part does
The example shows three client patterns: configuration, discovery and invocation.
| Pattern | Code | What it does |
|---|---|---|
| Configuration | Client(config) |
Declares one or more servers by name, each with a URL, transport and optional headers. |
| Discovery | list_tools() |
Returns Tool objects with names, JSON Schema inputs and metadata such as tags. |
| Invocation | call_tool(name, args) |
Awaits the server’s response and returns a CallToolResult, not a raw string. |
The example server does not check the Authorization header. The header in the config only shows where credentials go for servers that require them.
When to use this pattern
Use a standalone client to inspect a server’s tools before integrating it, to call MCP tools from a script, or to understand MCP mechanics. Do not use it when an LLM should decide which tools to call. In that case plug MCP into a 10xGraph agent, which manages the client for you, as in MCP ReAct agent. The server side is covered in MCP server.
Common issues and how to fix them
| Issue | Fix |
|---|---|
| Connection refused | Start the server first and check that the URL in the config matches its host, port and /mcp path. |
| 401 or auth errors | Match the headers to what your server expects. The example server needs none. |
ModuleNotFoundError for fastmcp, mcp or dotenv |
Run pip install fastmcp python-dotenv. |
| Empty tag list | The tool was registered without tags, or your FastMCP version stores metadata differently. Print i.model_dump() to check. |
What to try next
Add a second server entry to the mcpServers config and list the tools from both. Then move to MCP ReAct agent to let an agent call these tools automatically.
Frequently asked questions
- Do I need a graph to call MCP tools?
- No. The FastMCP Client connects to a server, lists tools and calls them on its own. A graph is only needed when an LLM should choose the tools.
- Does the example server check the Authorization header?
- No. The example server accepts any request. The header in the client config shows where credentials go for servers that require them.