How does MCP become the "USB-C interface" of AI Agent? Attached with the construction tutorial

The MCP protocol provides a standardized solution for AI models to communicate with external data sources and tools, greatly simplifying the development process.
Core content:
1. The concept and role of MCP: standardizing the communication between AI models and external systems
2. MCP architecture and functions: client-server model, supporting tools, resources and prompts
3. MCP communication mechanism: based on JSON-RPC 2.0 protocol, supporting Stdio and HTTP+SSE
What is MCP?
MCP (Model Context Protocol) is an open standard that aims to standardize the communication between large language models (LLMs) and external tools and data sources . It adopts a client-server architecture and supports multiple communication protocols and transmission mechanisms to achieve structured, multi-round, and scalable context exchange. Just like a USB-C port for AI applications, it provides a standardized way to connect AI models to different data sources and tools.
Before MCP, developers had to build custom connections for each data source or tool required for each AI application—a time-consuming and repetitive process.
MCP adopts a client-server architecture , with the AI model acting as the client and communicating with the MCP server through the MCP protocol. The MCP server is responsible for interacting with external data sources or tools, and returning the acquired data to the client after formatting it according to the MCP protocol specification. This design enables the AI model to dynamically obtain the required contextual information and perform a wider range of tasks, greatly shortening development time and reducing development complexity.
MCP Architecture
MCP follows a client-server architecture where:
- Host
(Host) is the LLM application (such as Claude Desktop or IDE) that initiates the connection. - Client
(MCP Client) Maintains a 1:1 connection with the server inside the host application. - server
(MCP Server) Provides context, tools, and prompts to clients.
MCP supports three functions:
- Tools
Functions that can be called by AI (such as sending emails, checking the weather). - Resources
Data that can be read (such as files, database records). - Prompts
Preset instruction templates to optimize task execution.
MCP transport communication
MCP's communication is based on the JSON-RPC 2.0 protocol, which is a lightweight remote procedure call protocol that uses the JSON format for data exchange. The protocol supports stateful connections, allowing multiple requests and responses in a session, and is suitable for multi-round conversations and complex tool call processes.
MCP supports the following two main transport mechanisms:
Standard Input/Output (Stdio)
• Applicable scenarios: Local integration where the client and server run on the same machine.
• Features: Uses standard input and output streams for communication, suitable for accessing the local file system or executing local scripts.
HTTP + Server-Sent Events (SSE)
• Applicable scenarios: Remote communication with distributed deployment of clients and servers.
• Features: The client sends a request to the server through HTTP POST , and the server pushes real-time messages to the client through SSE , supporting real-time data streaming and event-driven communication.
In the MCP architecture, the communication process is as follows: 1. Client: Located in the host application (such as a chatbot, IDE assistant), responsible for building requests and sending them to the MCP server.
Server: Provides access interfaces to external data sources or tools, receives client requests, and returns structured responses after processing.
Communication protocol: The client and server communicate via the JSON-RPC 2.0 protocol, and the transport mechanism can be Stdio or HTTP + SSE.
MCP provides a unified communication protocol and a variety of transmission mechanisms to support efficient integration between large language models and external tools and data sources. Through standardized structure and flexible transmission methods, MCP enables developers to build more powerful and scalable AI applications.
MCP and Function Calling (Tool Call)
Function Calling is a mechanism for calling functions within a model. It is implemented by LLM providers (such as OpenAI, Anthropic) that allows the model to generate structured function call requests based on user input. There may be differences between different platforms. It is suitable for tasks with clear boundaries and clear descriptions, such as data extraction, classification, or external API calls. The code has poor adaptability and reusability (we need to encode each function into the program).
The MCP (Model Context Protocol) is a universal communication protocol between models and external systems. It enables seamless integration of different AI models and external systems, reducing adaptation costs. It is better at handling complex, multi-step dialogue scenarios, especially in scenarios that need to maintain contextual consistency and dynamically adapt to user needs.
MCP in action
1. Build Weather MCP Server & Client locally
Building the MCP Weather Server
from typing import Any
import httpx
from mcp . server . fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP ( "weather" )
Defining MCP Server tools
# Get weather alerts for a state in the United States
@mcp . tool ( )
async def get_alerts ( state : str ) - > str :
"" " Get weather alerts for a US state .
Args :
state : Two - letter US state code ( e . g . CA , NY )
"" "
url = f "{NWS_API_BASE}/alerts/active/area/{state}"
data = await make_nws_request ( url )
if not data or "features" not in data :
return "Unable to fetch alerts or no alerts found."
if not data [ "features" ] :
return "No active alerts for this state."
alerts = [ format_alert ( feature ) for feature in data [ "features" ] ]
return "\n---\n" . join ( alerts )
# Get weather alerts for a location
@mcp . tool ( )
async def get_forecast ( latitude : float , longitude : float ) - > str :
"" " Get weather forecast for a location .
Args :
latitude : Latitude of the location
longitude : Longitude of the location
"" "
# First get the forecast grid endpoint
points_url = f "{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request ( points_url )
if not points_data :
return "Unable to fetch forecast data for this location."
# Get the forecast URL from the points response
forecast_url = points_data [ "properties" ] [ "forecast" ]
forecast_data = await make_nws_request ( forecast_url )
if not forecast_data :
return "Unable to fetch detailed forecast."
# Format the periods into a readable forecast
periods = forecast_data [ "properties" ] [ "periods" ]
forecasts = [ ]
for period in periods [ : 5 ] : # Only show next 5 periods
forecast = f "" "
{ period [ 'name' ] } :
Temperature : { period [ 'temperature' ] } ° { period [ 'temperatureUnit' ] }
Wind : { period [ 'windSpeed' ] } { period [ 'windDirection' ] }
Forecast : { period [ 'detailedForecast' ] }
"" "
forecasts.append ( forecast )
return "\n---\n" . join ( forecasts )
use main
Method to start Weather MCP Server
if __name__ == "__main__" :
#Initialize and run the server
mcp . run ( transport = 'stdio' )
Define the Weather MCP Client Class:
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession , StdioServerParameters
from mcp . client . stdio import stdio_client
from anthropopic import Anthropic
from dotenv import load_dotenv
load_dotenv ( ) # load environment variables from . env
class MCPClient :
def __init__ ( self ) :
#Initialize session and client objects
self . session : Optional [ ClientSession ] = None
self . exit_stack = AsyncExitStack ( )
self . anthropic = Anthropic ( )
# methods will go here
Connecting to MCP Server
async def connect_to_server ( self , server_script_path : str ) :
"" " Connect to an MCP server
Args :
server_script_path : Path to the server script ( . py or . js )
"" "
is_python = server_script_path . endswith ( '.py' )
is_js = server_script_path . endswith ( '.js' )
if not ( is_python or is_js ) :
raise ValueError ( "Server script must be a .py or .js file" )
command = "python" if is_python else "node"
server_params = StdioServerParameters (
command = command ,
args = [ server_script_path ] ,
env = None
)
stdio_transport = await self . exit_stack . enter_async_context ( stdio_client ( server_params ) )
self . stdio , self . write = stdio_transport
self . session = await self . exit_stack . enter_async_context ( ClientSession ( self . stdio , self . write ) )
await self . session . initialize ( )
# List available tools
response = await self . session . list_tools ( )
tools = response . tools
print ( "\nConnected to server with tools:" , [ tool . name for tool in tools ] )
Query processing logic
async def process_query ( self , query : str ) - > str :
"" "Process a query using Claude and available tools" ""
messages = [
{
"role" : "user" ,
"content" : query
}
]
response = await self . session . list_tools ( )
available_tools = [ {
"name" : tool . name ,
"description" : tool . description ,
"input_schema" : tool . inputSchema
} for tool in response . tools ]
# Initial Claude API call
response = self . anthropic . messages . create (
model = "claude-3-5-sonnet-20241022" ,
max_tokens = 1000 ,
messages = messages ,
tools = available_tools
)
# Process response and handle tool calls
final_text = [ ]
assistant_message_content = [ ]
for content in response . content :
if content . type 'text' :
final_text . append ( content . text )
assistant_message_content . append ( content )
elif content .type 'tool_use ' :
tool_name = content . name
tool_args = content . input
# Execute tool call
result = await self . session . call_tool ( tool_name , tool_args )
final_text . append ( f "[Calling tool {tool_name} with args {tool_args}]" )
assistant_message_content . append ( content )
messages . append ( {
"role" : "assistant" ,
"content" : assistant_message_content
} )
messages . append ( {
"role" : "user" ,
"content" : [
{
"type" : "tool_result" ,
"tool_use_id" : content . id ,
"content" : result . content
}
]
} )
# Get next response from Claude
response = self . anthropic . messages . create (
model = "claude-3-5-sonnet-20241022" ,
max_tokens = 1000 ,
messages = messages ,
tools = available_tools
)
final_text . append ( response . content [ 0 ] . text )
return "\n" . join ( final_text )
Note : The request and response structures of different LLMs are different. DeepSeek and Qianwen models follow the OpenAI specification, while Claude and Gemini's responses are different from OpenAI. For example:
Claude response.content[0].text
with OpenAI response.choices[0].message.content
Interactive chat interface
async def chat_loop ( self ) :
"" "Run an interactive chat loop "" "
print ( " \n MCP Client Started!" )
print ( "Type your queries or 'quit' to exit." )
while True:
try:
query = input ( " \n Query: " ) .strip ( )
if query.lower ( ) == 'quit' :
break
response = await self.process_query ( query )
print ( " \n " + response )
except Exception as e:
print ( f " \n Error: {str(e)}" )
async def cleanup ( self ) :
"" "Clean up resources "" "
await self.exit_stack.aclose ( )
test
async def main ( ) :
if len ( sys . argv ) < 2 :
print ( "Usage: python client.py <path_to_server_script>" )
sys.exit ( 1 )
client = MCPClient ( )
try :
await client . connect_to_server ( sys . argv [ 1 ] )
await client . chat_loop ( )
finally :
await client . cleanup ( )
if __name__ == "__main__" :
import sys
asyncio . run ( main ( ) )
Start the Client
python client . py . / weather . py
# Relative path
uv run client .py ./server/weather.py
# Absolute path
uv run client .py / Users / username / projects / mcp - server / weather .py
# Windows path ( either format works )
uv run client .py C : /projects/mcp-server/weather.py
uv run client .py C : \\projects\\mcp - server \\ weather .py
2. Start GitHub MCP Server
MCP Server Github provides many ready-made MCP Servers. GitHub MCP Server is a Model Context Protocol (MCP) server that can be seamlessly integrated with the GitHub API to provide advanced automation and interaction capabilities for developers and tools.
Run GitHub MCP Server directly using npx . You need to create a GitHub personal access token
( https://github.com/settings/tokens)
npx - y @modelcontextprotocol / server - github
Use the official MCP Inspector tool to test and debug the MCP Server.
npx @modelcontextprotocol / inspector npx - y @modelcontextprotocol / server - github
Browser access http://localhost:5173
3.LLM integrates GitHub MCP Server
There are many ways to integrate LLM with MCP Server. You can customize it or use the existing library directly.
usemcp-use
Integrate LLM and MCP,mcp-use
It is an open source Python library that makes it very easy to connect any LLM to any MCP server, both local and remote.
import asyncio
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from mcp_use import MCPAgent , MCPClient
"" "
pip install mcp - use
"" "
async def main ( ) :
# Load environment variables
load_dotenv ( )
# Create configuration dictionary
config = {
"mcpServers" : {
"github" : {
"command" : "npx" ,
"args" : [ "-y" , "@modelcontextprotocol/server-github" ] ,
"env" : {
"GITHUB_PERSONAL_ACCESS_TOKEN" : "<GITHUB PERSONAL ACCESS TOKEN>"
}
}
}
}
# Create MCPClient from configuration dictionary
client = MCPClient . from_dict ( config )
# Create LLM
llm = ChatOpenAI ( api_key = os . getenv ( "DEEPSEEK_API_KEY" ) ,
base_url = "https://api.deepseek.com" ,
model = "deepseek-chat" )
# Create agent with the client
agent = MCPAgent ( llm = llm , client = client , max_steps = 30 )
# Run the query
result = await agent . run (
"search ai-agent-demo repo" ,
)
print ( f "\nResult: {result}" )
if __name__ == "__main__" :
asyncio . run ( main ( ) )