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

Written by
Caleb Hayes
Updated on:June-13th-2025
Recommendation

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

Yang Fangxian
Founder of 53A/Most Valuable Expert of Tencent Cloud (TVP)
MCP Explained: The New Standard Connecting AI to Everything | by EdwinLisowski | Apr, 2025 | Medium" class="rich_pages wxw-img" data-ratio="0.588888888888889" data-type="png" data-w="1080" data-imgfileid="100001488">

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:

  1. 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.

  2. 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.

  1. Server: Provides access interfaces to external data sources or tools, receives client requests, and returns structured responses after processing.

  2. 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-useIt is an open source Python library that makes it very easy to connect any LLM to any MCP server, both local and remote.

The following code uses Deepseek LLM to integrate Github MCP Server:
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 ( ) )