One Agent, Four Servers: Wiring Maps, Browsers, and Files into a Single AI Loop
Mastering MCP Multi-Server Architecture from Scratch: Let AI Control Maps, Browsers, and the File System Simultaneously
Foreword
Imagine this: you just say in natural language, "Find hotels near Beijing South Railway Station and save the results as a screenshot locally." The AI can automatically call the Amap to find hotels, control the browser to take a screenshot, and then write to a local file — all without you manually operating any tools.
This is the transformation brought by MCP (Model Context Protocol) . This article, based on a real project, takes you from zero to understanding MCP's multi-server architecture and provides a hands-on guide to implementing an AI Agent that connects to 4 MCP Servers simultaneously.
1. What is MCP? Why is it Important?
1.1 Understanding in One Sentence
MCP is the "USB interface protocol" for AI. Just as USB allows a computer to connect peripherals like keyboards, mice, and flash drives, MCP allows AI to connect to various tools like maps, browsers, file systems, and databases.
1.2 Core Value: Reusability
With the MCP protocol, anyone can develop an MCP Server based on this protocol, which can then be directly reused.
This is the greatest charm of MCP — develop once, use everywhere. Amap's official team developed a Maps MCP Server; you don't need to wrap the map API yourself, just take it and use it directly.
1.3 Two Connection Methods
| Connection Method | Transport Protocol | Applicable Scenarios | Example |
|---|---|---|---|
| Remote HTTP | HTTP/SSE | Third-party services, cloud APIs | Amap MCP |
| Local stdio | Standard Input/Output | Local tools, browser control | Chrome DevTools, FileSystem |
Knowledge Point 1: The MCP Protocol
What is MCP: Model Context Protocol, an open-source standard protocol defined by Anthropic that specifies the communication standard between AI models and external tools. It allows LLMs to discover and invoke various external tools in a unified way, without writing custom glue code for each tool.
Core Concepts:
- Server: A process that provides tools (can be a local program or a remote service)
- Client: A client that connects to the Server and retrieves the tool list
- Tool: A specific capability exposed by the Server (e.g., "query latitude and longitude", "open a webpage", "write a file")
Why it's important: Before MCP, enabling AI to call external tools required writing separate adapter code for each tool. MCP unifies this process — just like USB unified the standard for connecting peripherals.
2. Project Architecture Overview
┌─────────────────────────────────────────────────────┐
│ AI Agent (DeepSeek) │
│ Understands natural language → Decides which tools to call │
└──────────────────────┬──────────────────────────────┘
│ bindTools()
┌──────────────────────▼──────────────────────────────┐
│ MultiServerMCPClient │
│ (LangChain MCP Adapter) │
│ Manages tools from multiple MCP Servers uniformly │
└──┬──────────┬──────────────┬──────────────┬─────────┘
│ HTTP │ stdio │ stdio │ stdio
┌──▼────┐ ┌───▼──────┐ ┌────▼──────┐ ┌───▼─────────┐
│ Amap │ │ Custom MCP│ │Chrome │ │ FileSystem │
│ MCP │ │ Server │ │DevTools MCP│ │ MCP │
│ (Remote) │ │ (Local) │ │ (Local) │ │ (Local) │
└────────┘ └──────────┘ └────────────┘ └─────────────┘
Core dependencies (package.json):
@langchain/mcp-adapters: LangChain's MCP adapter layer, providingMultiServerMCPClient@langchain/openai: For model calls compatible with the OpenAI protocol (connected to DeepSeek here)@modelcontextprotocol/sdk: The official MCP SDK, used for custom MCP Servers
3. Creating Your Own MCP Server
Start with the simplest thing — writing a local Server using the MCP SDK. Code is in my-mcp-server.mjs:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
// 1. Create a Server instance
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
// 2. Register a tool: addition
server.tool(
'add',
'Calculate the sum of two numbers',
{
a: z.number().describe('The first number'), // ← zod defines the parameter schema
b: z.number().describe('The second number'),
},
async ({ a, b }) => ({
content: [{ type: 'text', text: String(a + b) }], // ← Returns standard format
})
);
// 3. Start via stdio, waiting for client connection
const transport = new StdioServerTransport();
await server.connect(transport);
Knowledge Point 2: The Three Elements of an MCP Server
Every MCP tool contains three core parts:
1. Name: A unique identifier, like
'add'. The AI uses the name to distinguish and select tools.2. Parameter Schema (input schema): Defines parameter types and descriptions using Zod. Zod is a schema validation library for TypeScript/JavaScript. The AI reads this schema to understand "what parameters this tool requires."
{ a: z.number().describe('The first number') }
z.number()indicates the parameter type is a number, and.describe()provides a parameter description for the AI.3. Callback Function (handler): The function that actually executes the business logic, returning data in the
{ content: [{ type: 'text', text: '...' }] }format.stdio Transport: The client runs the MCP Server by launching a child process, and the two exchange JSON messages via standard input/output (stdin/stdout). This means an MCP Server can be a command-line program written in any language.
4. AI Agent Client: Connecting to Multiple MCP Servers Simultaneously
The most exciting part is here — mcp-test.mjs. It uses a single MultiServerMCPClient to manage all MCP Servers uniformly:
const mcpClient = new MultiServerMCPClient({
mcpServers: {
// Type 1: Remote HTTP MCP
'amap': {
"url": "https://mcp.amap.com/mcp?key=YOUR_KEY"
},
// Type 2: Local stdio MCP (runs a JS file)
'my-mcp-server': {
command: 'node',
args: ['./src/my-mcp-server.mjs']
},
// Type 3: Local stdio MCP (runs an npm package via npx)
'chrome-devtools': {
command: 'npx',
args: ['-y', 'chrome-devtools-mcp']
},
// Type 4: File System MCP
'filesystem': {
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', './output']
}
}
});
// Get all tools from all Servers with one line of code!
const tools = await mcpClient.getTools();
Configuration comparison for the four types of Servers:
| MCP Server | Type | Configuration Method |
|---|---|---|
| Amap | HTTP Remote | Fill in the URL directly; the SDK handles the connection automatically |
| Custom Server | stdio Local | command: 'node' + script path |
| Chrome DevTools | stdio Local | npx -y pulls and runs the npm package |
| FileSystem | stdio Local | Same as above, followed by the allowed access directory |
Knowledge Point 3: How MultiServerMCPClient Works
MultiServerMCPClient is the aggregation client provided by the LangChain MCP Adapter.
Workflow:
- Reads each MCP Server from the configuration
- For HTTP type → Sends an HTTP request to get the tool list
- For stdio type → Starts a child process and communicates via stdin/stdout
- Merges the tools from all Servers into a unified tool list
- Injects the tools into the AI model via
model.bindTools(tools)Key Advantage: The AI doesn't need to know which tool comes from which Server — it just sees a unified tool list and calls them as needed. This is like the "hardware abstraction layer" of an operating system.
5. AI Agent Loop: Letting the Model Make Autonomous Decisions
After having the tool list, how do you get the AI to call them autonomously? The core is the Agent Loop:
async function runAgentWithTools(query, maxIterations = 10) {
const messages = [new HumanMessage(query)];
for (let i = 0; i < maxIterations; i++) {
// Step 1: Send the conversation history to the model
const response = await modelWithTools.invoke(messages);
messages.push(response);
// Step 2: If the model returns text directly → Task complete!
if (!response.tool_calls || response.tool_calls.length === 0) {
return response.content;
}
// Step 3: The model says to call tools → Execute them one by one
for (const tool_call of response.tool_calls) {
const foundTool = tools.find(t => t.name === tool_call.name);
const result = await foundTool.invoke(tool_call.args);
// Step 4: Tell the model the tool execution result, letting it decide the next step
messages.push(new ToolMessage({
content: result,
tool_call_id: tool_call.id,
}));
}
// Back to Step 1, the model continues reasoning based on the results...
}
}
Knowledge Point 4: The Execution Flow of an Agent Loop
The whole process is like an "intelligent while loop":
User: "Find hotels near Beijing South Station, write to a file" ↓ Round 1: AI calls amap.search("Hotels near Beijing South Station") ← I need location data ↓ (Tool returns: Hotel list) Round 2: AI calls amap.get_detail(hotel1_id) ← I need detailed info ↓ (Tool returns: Name, address, rating) Round 3: AI calls filesystem.write("hotels.txt", result) ← Data is sufficient, write to file ↓ (Tool returns: Write successful) Round 4: AI returns "Done! Hotel info has been written to hotels.txt" ← Task complete, stop loopKey Design Points:
- ToolMessage: The tool execution result is wrapped as a ToolMessage and appended to the message history, so the model can see "the result of the last tool call"
- maxIterations: A safety valve to prevent infinite loops (e.g., if a tool keeps failing)
- Model Autonomous Decision-Making: Which tool to call at each step and what parameters to pass are entirely decided by the model
6. Practical Demonstration
Scenario 1: Map + File System Integration
await runAgentWithTools(`
Help me find hotels near Beijing South Railway Station, find the nearest 3.
For each hotel, write the name, address, rating, and other info
into the hotels_near_beijing_south_station.txt file.
`);
The AI will automatically: call the Amap MCP to search → get details → call the FileSystem MCP to write to a file.
Scenario 2: Browser Control
await runAgentWithTools('Open the Baidu homepage and tell me what the page title is');
The AI automatically calls the Chrome DevTools MCP's navigate_page → take_snapshot tools.
Knowledge Point 5: The Role of Chrome DevTools MCP
chrome-devtools-mcp is an npm package that provides 29 browser control tools:
Category Tools Page Navigation navigate_page,new_page,close_page,select_pageElement Interaction click,fill,hover,drag,type_text,press_keyData Retrieval take_screenshot,take_snapshot,evaluate_scriptDebugging & Analysis list_console_messages,list_network_requests,performance_start_traceIt allows you to control a browser using natural language — "Open Taobao and search for iPhone", "Take a screenshot and save it", "Check console errors", the AI can help you complete all of these.
Note: Requires Chrome or Edge browser installed locally (any Chromium-based browser will do).
Knowledge Point 6: Environment Variable Management
The project uses dotenv to manage sensitive configuration (.env):
DEEPSEEK_API_KEY=sk-xxx DEEPSEEK_API_BASE_URL=https://api.deepseek.com/v1 DEEPSEEK_MODEL=deepseek-v4-flashIn the code, it's automatically loaded via
import 'dotenv/config', then read usingprocess.env.XXX. Never hardcode API Keys in your code.
7. Summary
Review of Core Points
- MCP is a tool protocol for AI: It unifies the communication method between AI and external tools, making tools reusable
- Two transport methods: HTTP (remote services) and stdio (local processes)
- MultiServerMCPClient: One client manages multiple MCP Servers simultaneously; the AI doesn't need to care about the tool's source
- Agent Loop: The AI cycles through "think → call tool → see result → think again" until the task is complete
- Creating a custom MCP Server only takes three steps: Create Server → Register tool (name + zod schema + handler) → Connect transport
File Index
| File | Purpose |
|---|---|
| readme.md | Project notes, recording application scenarios and design ideas |
| my-mcp-server.mjs | Custom MCP Server example (add/get_current_time/reverse_string) |
| mcp-test.mjs | AI Agent client, connecting multiple MCP Servers + Agent Loop |
| package.json | Project dependencies |
| .env | Environment variables (API Key) |
Remember MCP in one sentence: It transforms AI from "just chatting" to "capable of doing anything" — and all you need to do is fill in the MCP Server configuration.
Welcome to discuss in the comments! If you've also written interesting MCP Servers, feel free to share 🚀