VOOZH about

URL: https://dev.to/wonderlab/agent-series-10-mcp-protocol-standardizing-the-tool-ecosystem-3lhe

⇱ Agent Series (10): MCP Protocol — Standardizing the Tool Ecosystem - DEV Community


More Tools, More Chaos

After you build an Agent, the first thing you usually do is give it tools: search, code execution, database queries, API calls…

With traditional Function Calling, tool definitions look like this:

@tool
def search_jira(query: str) -> str:
 """Search Jira tickets"""
 ...

@tool
def query_database(sql: str) -> str:
 """Execute a SQL query"""
 ...

agent = create_react_agent(model=llm, tools=[search_jira, query_database, ...])

This works great — until you start building a second Agent.

The problem: Agent B also needs search_jira. You import the function or copy it over. Then Agent C, Agent D. Tool definitions start drifting across the codebase. One day you update search_jira's logic and need to find every file that references it — scattered across four different files.

This is the problem MCP (Model Context Protocol) solves: turning tools from "each Agent defines its own" into "a shared service any Agent connects to via protocol".


MCP's Three-Layer Architecture

MCP breaks tool invocation into three roles:

┌────────────────────────────────────────────────────────────────┐
│ MCP Architecture │
├──────────────────┬──────────────────────────────────────────────┤
│ Host │ The environment hosting the Agent: │
│ │ Claude Desktop, Claude Code, custom apps │
│ │ Contains one or more MCP Clients │
├──────────────────┼──────────────────────────────────────────────┤
│ Client │ Protocol client embedded in the Host │
│ │ Manages connections and communication │
│ │ with MCP Servers │
├──────────────────┼──────────────────────────────────────────────┤
│ Server │ Independent process exposing Tools │
│ │ Can also expose Resources and Prompts │
└──────────────────┴──────────────────────────────────────────────┘

Transport options:
 Local: stdio (subprocess stdin/stdout)
 Remote: HTTP + SSE (Server-Sent Events)

The core architectural difference from Function Calling is that the Server is an independent process:

  • Function Calling: tools are Python functions, written in Agent code, called in-process
  • MCP: tools are independent services, called cross-process via JSON-RPC

An independent process means: tools can be implemented in any language, shared by any number of Agents, and updated without touching Agent code.


Demo 1: Traditional Function Calling's Limitations

@lc_tool
def calculator(expression: str) -> str:
 """Evaluate a simple arithmetic expression."""
 ...

@lc_tool
def text_stats(text: str) -> str:
 """Return word count, sentence count, and character count."""
 ...

@lc_tool
def weather_mock(city: str) -> str:
 """Return mock weather data for a city."""
 ...

traditional_tools = [calculator, text_stats, weather_mock]
agent = create_react_agent(model=llm, tools=traditional_tools)

Three test questions, real execution:

Q: What is 2 ** 10 + 100 / 4?
A: The result of 2 ** 10 + 100 / 4 is 1049.0.

Q: Analyze this text: 'Python is elegant. It is readable. Everyone loves it!'
A: 9 words, 3 sentences, 53 characters.

Q: What's the weather in Beijing?
A: sunny, 25°C, humidity 40%.

Works perfectly. The problem is architectural:

Tools defined in THIS file: ['calculator', 'text_stats', 'weather_mock']

If Agent B also needs these tools: copy-paste or re-import
If you update calculator's logic: find every Agent file that uses it
If the tools are written in TypeScript: this approach doesn't work at all

Demo 2: MCP Server — Dynamic Tool Discovery

The same three tools implemented as a standalone MCP Server using FastMCP:

# tools_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("demo-tools")

@mcp.tool()
def calculator(expression: str) -> str:
 """Evaluate a simple arithmetic expression (e.g. '2 ** 10', '100 / 7')."""
 ...

@mcp.tool()
def text_stats(text: str) -> str:
 """Return word count, sentence count, and character count for the given text."""
 ...

@mcp.tool()
def weather_mock(city: str) -> str:
 """Return mock weather data for a city (demo only — not real data)."""
 ...

if __name__ == "__main__":
 mcp.run(transport="stdio")

Client side — connect and discover:

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(command="python", args=["tools_server.py"])

async with stdio_client(server_params) as (read, write):
 async with ClientSession(read, write) as session:
 await session.initialize()

 # Dynamic discovery — no tool names hardcoded in client code
 tools_result = await session.list_tools()

Measured list_tools() response:

Server: demo-tools
Discovered 3 tools:
 ● calculator — Evaluate a simple arithmetic expression (e.g. '2 ** 10', '100 / 7').
 ● text_stats — Return word count, sentence count, and character count for the given text.
 ● weather_mock — Return mock weather data for a city (demo only — not real data).

Key point: the client code contains no mention of calculator, text_stats, or weather_mock. It only called list_tools() — the tool catalog was obtained dynamically from the Server.

Direct tool calls (no LLM):

calculator('2 ** 10 + 100 / 4')  2 ** 10 + 100 / 4 = 1049.0
weather_mock('Shanghai')  {"city": "Shanghai", "temp": 22, "condition": "cloudy", "humidity": 75}
text_stats(...)  {"words": 9, "sentences": 3, "chars": 53}

Demo 3: LLM Agent Using MCP Tools

langchain-mcp-adapters automatically converts MCP tool schemas into LangChain Tool objects. The Agent code looks almost identical to Demo 1 — the difference is where the tools come from:

from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient(
 {
 "demo-tools": {
 "command": "python",
 "args": ["tools_server.py"],
 "transport": "stdio",
 }
 }
)
mcp_tools = await client.get_tools()
# → ['calculator', 'text_stats', 'weather_mock']
# Tool definitions live in the Server, not in this file

agent = create_react_agent(model=llm, tools=mcp_tools)
result = await agent.ainvoke({"messages": [HumanMessage(multi_q)]})

Note: MCP tools are async. You must use await agent.ainvoke() — synchronous agent.invoke() throws NotImplementedError.

Three-question multi-tool test, real results:

Questions:
 1. What is sqrt(144) + 2 ** 8?
 2. What's the weather in Shenzhen?
 3. Count the stats for: 'MCP is a protocol. It standardizes tools. Agents love it!'

Answers:
 1. The result of sqrt(144) + 2 ** 8 is 260.
 2. (LLM called weather_mock, but added a "not real-time data" disclaimer
 because the tool description says "demo only")
 3. word count: 15, sentence count: 1, character count: 52

All three CallToolRequest events appeared in the Server log — the Agent routed all three questions through MCP.

The weather disclaimer is a useful teaching moment: the LLM reads description carefully. When the tool says "demo only — not real data", the LLM correctly qualifies its answer. Good tool descriptions make LLMs better decision-makers.


MCP vs Function Calling: Comparison Matrix

Dimension Function Calling MCP
──────────────────────────────────────────────────────────────────────
Tool definition In Agent code In independent MCP Server
Tool discovery Hardcoded import list_tools() (dynamic)
Multi-agent reuse Copy-paste or re-import All Agents connect to same Server
Update tool logic Edit every Agent file Edit Server only
Cross-language Same language only Any language (JSON-RPC)
Invocation In-process function call Cross-process JSON-RPC
Startup overhead None subprocess launch
Best fit Single Agent / small projects Multi-Agent / team collaboration

The MCP Ecosystem

Much of MCP's value comes from the growing catalog of existing servers:

Official MCP Servers (@modelcontextprotocol/):
 server-filesystem — local file read/write
 server-github — GitHub repo, issues, PR operations
 server-postgres — PostgreSQL queries
 server-brave-search — web search (requires Brave API key)

Community maintained:
 Search "awesome-mcp-servers" for the full list
 Includes: Jira, Slack, Notion, Linear, Figma, and dozens more

Claude Code's tools are MCP:
 → file read/write, terminal commands, code search…
 → all run through this same protocol

MCP Server Development Checklist

Tool Design

  • [ ] Each tool does one thing with a clear input/output schema
  • [ ] Write description carefully — the LLM uses it to decide whether to call the tool
  • [ ] Dangerous operations (file write, command execution) need allowlists or confirmation

Server Implementation

  • [ ] FastMCP for rapid development; use low-level mcp.server.Server for fine-grained control
  • [ ] Tool functions return strings (MCP protocol's TextContent type)
  • [ ] Error handling: return descriptive error strings, don't let exceptions propagate to the protocol layer

Transport Selection

  • [ ] Local tools → stdio (subprocess, zero config)
  • [ ] Remote/shared tools → HTTP + SSE (needs auth and network config)
  • [ ] Production → consider connection pooling and timeout settings

Client Integration

  • [ ] Use langchain-mcp-adapters for LangChain/LangGraph integration
  • [ ] MCP tools are async: use await agent.ainvoke(), not agent.invoke()
  • [ ] Multiple servers: MultiServerMCPClient manages all connections centrally

Summary

Five core takeaways:

  1. MCP solves tool management, not just tool calling: Function Calling is per-agent tool binding; MCP is cross-agent tool services
  2. Dynamic discovery is the key capability: list_tools() lets an Agent find and use tools without knowing their names in advance
  3. Independent process enables cross-language tools: a JavaScript MCP Server is callable by a Python Agent — JSON-RPC is language-agnostic
  4. Claude Code's tools are MCP: every time you use Claude Code to read a file or run a command, that's this exact protocol in action
  5. Async is the MCP tool contract: MCP tools execute via async calls; LangChain integration requires ainvoke(), not invoke()

Up next: A2A Protocol and Agent Networks — how do Agents collaborate with each other? MCP handles Agent ↔ Tool. A2A handles Agent ↔ Agent.


References


Find more useful knowledge and interesting products on my Homepage