Building MCP Agents & Clients with mcp-use
Using the open-source mcp-use framework to build an MCP-enabled agent in ~6 lines of code, solve the tool-overload problem with the Server Manager, and create/inspect MCP clients.
Establish Transport pipe connection
Host client (IDE or AI assistant) connects to the server process over standard output streams.
▶📚 Prerequisites(2)
🎓 Learning objectives
- •Build a minimal MCP-enabled agent using mcp-use in a handful of lines of code
- •Identify the 3 failure modes of tool overload (hallucinated tool names, similar-tool confusion, degraded decision quality)
- •Enable and explain what the Server Manager does to fix tool overload
- •Create an MCP client from a config file or a Python dictionary, and inspect it for debugging
What is it?
mcp-use is an open-source framework that removes the low-level protocol plumbing from building MCP-enabled agents, clients, and servers. Instead of manually implementing capability handshakes, transport management, and tool-schema wiring, mcp-use lets you build a working MCP-enabled agent in about 6 lines of code: it sets up the MCP client, connects to one or more servers, discovers available tools, and exposes them to the LLM in a structured way — the agent then decides when to call a tool while the framework manages capability loading and communication under the hood.
Why it exists
While the MCP specification defines the wire protocol (JSON-RPC, capability negotiation, the 6 core primitives), actually wiring an agent up to speak that protocol correctly — handling connections, capability discovery, streaming tool calls, and multi-server coordination — is real, repetitive engineering work every team would otherwise have to redo. mcp-use exists to be the reusable implementation layer sitting on top of the raw protocol, the same way a web framework sits on top of raw HTTP — you get the protocol's benefits without hand-rolling its plumbing every time.
Problem it solves
mcp-use solves the 'protocol boilerplate' problem of building MCP agents/clients/servers from scratch. It also solves a very specific, commonly-hit problem once an agent is connected to more than one MCP server: tool overload. When LLMs gain access to many server tools at once, three predictable failure modes appear — tool-name hallucination (the model invents a tool that doesn't exist, especially when the tool list is large or poorly named), confusion between similar tools (when a server exposes several tools with overlapping responsibilities, the model struggles to choose correctly), and degraded decision quality with large toolsets (too many tools at once increases cognitive load, leading to inconsistent selection or unnecessary calls). mcp-use's Server Manager exists specifically to solve this.
Intuition
Building an MCP agent without mcp-use is like wiring up a phone system by hand — you have to implement dial tones, call routing, and signal protocols yourself before you can even make a call. mcp-use is like buying a phone that already handles all of that, so you just dial a number and talk. The Server Manager, meanwhile, is like a good executive assistant who doesn't hand you every folder in the filing cabinet the moment you ask a question — instead, they figure out which folder is actually relevant to your current task and hand you just that one, so you're not overwhelmed sorting through irrelevant material.
Analogy
Tool overload is like a new employee's first day where someone hands them the keys to every room in the building, a directory of 200 tools, and says 'good luck figuring out which ones you need.' The Server Manager is like a smart office layout where doors only unlock as you walk toward the room you actually need for your current task — you're never confronted with more options than are relevant right now, and the system adapts as your task changes.
Technical explanation
Creating an agent: mcp-use's client setup creates an MCP client, connects it to a server (e.g., a Playwright browser-automation server in this course's example), wraps that server's capabilities as LLM-callable tools, and passes them to an LLM-powered agent — from there, the LLM can request tool calls naturally during its reasoning, while mcp-use handles execution and streaming of results back into the conversation.
The Server Manager: when enabled via use_server_manager=True, the Server Manager loads tools dynamically (only when needed, not all upfront), discovers which server is appropriate for the current task, keeps the active tool list intentionally small and focused (reducing model overwhelm), updates the available tools in real time as servers connect or disconnect, and provides semantic search over all available tools across every connected server — so instead of an LLM seeing a flat list of 47 tools, it effectively queries 'what tool do I need for this?' and the Server Manager surfaces only the relevant candidates. This makes the Server Manager the orchestrator deciding which server to activate, which tools to load, and when to surface them, resulting in clearer tool selection and more stable agent behavior across multi-server environments.
Creating an MCP client: every mcp-use agent embeds an MCP client, the component responsible for all communication between the agent and any MCP server — connecting to servers, performing the initial capability handshake, streaming tool calls and responses, retrieving resources, receiving notifications, and routing elicitation requests back to the user/host. mcp-use provides two ways to create a client: (1) loading configuration from a file, ideal when working across multiple environments or keeping server settings version-controlled, and (2) creating from a Python dictionary, mirroring the same structure but allowing programmatic customization inside code. Although the agent manages the client internally, mcp-use still allows direct client inspection — useful for debugging capability discovery or understanding exactly which tools are currently available.
Architecture
The mcp-use agent stack: [Agent] embeds an [MCP Client], which connects (via stdio or HTTP+SSE transport) to one or more [MCP Servers]. Optionally, a [Server Manager] sits between the Client and the Agent's tool-selection logic, performing dynamic tool loading and semantic search rather than exposing every server's full tool list flatly. Client creation supports two configuration paths: a versioned config file (multi-environment friendly) or an inline Python dictionary (programmatic, code-driven).
Workflow
- Install mcp-use and identify which MCP server(s) your agent needs to connect to (e.g., a Playwright server for browser automation, a filesystem server, a database server).
- Create an MCP client, either by loading a config file (for version-controlled, multi-environment setups) or by passing a Python dictionary (for programmatic setups).
- Wrap the connected server's capabilities as agent-usable tools and pass them into an LLM-powered agent — this is the core ~6-line agent creation flow.
- If connecting to more than one server, or if any single server exposes a large number of tools, enable the Server Manager (use_server_manager=True) rather than exposing everything flatly.
- Test the agent with realistic multi-step tasks and watch for the 3 tool-overload symptoms (hallucinated tool names, confusion between similar tools, inconsistent selection) — if you see any of them and haven't enabled the Server Manager, that's your signal to do so.
- Use the client inspection utilities to debug capability discovery issues — confirm the tools your agent thinks are available actually match what the connected servers are advertising.
Example
from mcp_use import MCPClient, MCPAgent from langchain_openai import ChatOpenAI
── The ~6-line agent (connects to a Playwright MCP server) ──
client = MCPClient.from_config_file('playwright_mcp.json') llm = ChatOpenAI(model='gpt-4o') agent = MCPAgent(llm=llm, client=client) result = await agent.run('Go to example.com and summarize the homepage') print(result)
── Same setup, but with the Server Manager enabled ──
(recommended once connecting to more than one server, or a server with many tools)
agent = MCPAgent(llm=llm, client=client, use_server_manager=True)
── Creating a client from a Python dict instead of a config file ──
client = MCPClient.from_dict({ 'mcpServers': { 'playwright': {'command': 'npx', 'args': ['@playwright/mcp']}, 'filesystem': {'command': 'npx', 'args': ['@mcp/filesystem', './data']}, } })
── Inspecting the client directly (debugging capability discovery) ──
sessions = await client.create_all_sessions() for name, session in sessions.items(): tools = await session.list_tools() print(f'{name}: {[t.name for t in tools]}')
Real-world usage
Teams building browser-automation agents (using an MCP server wrapping Playwright or Puppeteer) commonly hit tool overload almost immediately, since browser-automation servers tend to expose dozens of granular actions (click, type, navigate, screenshot, wait, scroll, etc.) — the Server Manager's semantic search over tools is specifically valuable here. Multi-server internal AI assistants (e.g., an internal tool connecting to a company's Slack MCP server, database MCP server, and ticketing-system MCP server simultaneously) are the primary real-world case for the Server Manager, since combining even 3-4 moderately-sized servers' tool lists quickly exceeds what a model can reliably navigate without dynamic loading. Config-file-based client creation is the standard approach for teams running the same agent across dev/staging/production environments with different server endpoints per environment, keeping server configuration out of application code.
Trade-offs
mcp-use trades some low-level control for dramatically less boilerplate — teams that need very specific custom protocol handling might still implement raw MCP client/server logic themselves, but most teams benefit from not re-solving already-solved plumbing problems. The Server Manager adds a layer of indirection (dynamic loading, semantic search over tools) that introduces a small amount of latency and complexity compared to a flat, always-available tool list — worth it once tool overload symptoms appear, but unnecessary overhead for a single-server agent with a handful of well-named tools where overload was never going to be a problem.
Visual explanation
Two diagrams.
Agent creation: [mcp-use.Client] → connects to → [MCP Server(s), e.g. Playwright] → discovers → [Available Tools] → wraps as → [LLM-callable tool schemas] → passed into → [Agent] — all in ~6 lines of code, hiding the connection/handshake/wiring details.
Tool overload + Server Manager: Without Server Manager — [Agent] sees [Server A's 15 tools] + [Server B's 20 tools] + [Server C's 12 tools] = 47 tools presented at once → model confusion/hallucination. With Server Manager (use_server_manager=True) — [Agent] sees only a small, task-relevant subset, dynamically loaded → [Server Manager] sits in between, doing semantic search over all 47 tools and surfacing only what's relevant to the current request, loading/unloading as servers connect or disconnect.
Advantages
- —
Reduces MCP agent/client creation from significant protocol-plumbing work to roughly 6 lines of code
- —
The Server Manager directly and automatically fixes the 3 named tool-overload failure modes without manual prompt engineering workarounds
- —
Two client-creation methods (config file, Python dict) support both version-controlled multi-environment setups and programmatic/dynamic setups
- —
Built-in client inspection makes debugging capability discovery issues significantly easier than manually tracing protocol messages
Disadvantages
- —
Adds a framework dependency compared to hand-rolling raw MCP protocol logic
- —
The Server Manager's dynamic tool loading and semantic search adds latency and complexity compared to a flat, always-loaded tool list
- —
Being a relatively new framework, mcp-use's API and feature set are still evolving
- —
For a single-server agent with few, well-differentiated tools, the Server Manager's benefits don't apply and it's unnecessary overhead
Common mistakes
- —
Connecting an agent to multiple MCP servers without enabling the Server Manager, then being surprised by tool-name hallucinations or inconsistent tool selection
- —
Assuming tool overload only happens with 'a lot' of servers — even a single server with 15-20 overlapping-responsibility tools can trigger the same failure modes
- —
Hardcoding server configuration directly in application code instead of using a config file, making multi-environment deployment harder than necessary
- —
Not using the client inspection utilities when debugging — manually guessing why a tool isn't available instead of directly querying what the client has actually discovered
- —
Enabling the Server Manager for every agent by default even when only connected to one small, well-scoped server, adding unnecessary indirection where a flat tool list would have worked fine
📂 Subtopics
Building an MCP-Enabled Agent in 6 Lines with mcp-use
mcp-use removes the low-level protocol plumbing — an MCP client, server connection, tool discovery, and LLM wiring all come together in about 6 lines of code.
~12 min
Tool Discovery and the Tool Overload Problem
When an agent discovers many tools across servers, 3 predictable failure modes appear: tool-name hallucination, confusion between similar tools, and degraded decision quality.
~12 min
Tool Invocation at Scale: The Server Manager
The Server Manager keeps an agent's active toolset intentionally narrow and context-driven — loading tools dynamically, only when needed, instead of exposing everything from every server at once.
~12 min
Configuring and Inspecting the MCP Client
Every mcp-use agent embeds an MCP client responsible for connections, handshakes, and streaming — configurable from a file or a Python dictionary, and directly inspectable for debugging.
~12 min