Model Context Protocol
I installed so many MCP servers on my AI agnets and they have magic.
Just some short notes to help myself understand what MCP is.
What it is
MCP is something that helps the AI models nteract with external services, data sources in a structured, controlled way. Instead of letting the AI models to explore how to work with the external things itself, the MCP servers provide the safe and deterministic tools with descriptions so that AI models can easily understand and use them.
In a typical setup, an AI agent (like Cline) sits between the model and the MCP server, passing tool definitions to the model and executing tool calls on its behalf.
Let’s say sending an email, when we first started using AI, it’s just the chatGPT webiste that helped me write, I’d still need to copy the output and send the email myself in Gmail.
Now we’ve got a Gmail MCP, which contains all the tools, including the script to call the Gmail’s SendMail API. The model only needs to generate the text, then delegate the job to the MCP server, and an email is sent.
How does an AI model interact with MCP servers
Let’s us sending an email as an exmple, and we are using Cline (or some random AI agent).
Step 1
First we have our MCP server running (locally or remotely), and it knows how to do “the thing”, sending an email via Gmail properly.
# on the MCP server
@mcp.tool()
def send_email(to: str, subject: str, body: str):
gmail_api.send(to, subject, body)
Step 2
The MCP server automatically exposes
{
"name": "send_email",
"description": "Send an email via Gmail",
"parameters": {
"to": "string",
"subject": "string",
"body": "string"
}
}
...
// more tools
Step 3
Cline connects to the MCP server, getting the tool list, the json file describing what are available.
Step 4
Cline talks to the AI model with your message and the tool definitions.
User message: "I need to send an email via gmail to my grandma@gmail.com to congrate her birthday"
The available tools on the MCP server:
1. ("name": "send_email", "description": "Send an email via Gmail", "parameters": ...)
2. ("name": "read_emails", "description": "read the unread emails", "parameters": ...)
Step 5
The model considers and responses this to Cline
{
"type": "tool_call",
"tool": "send_email",
"arguments": {...}
}
Step 6
Cline catches it!
Cline has something like this implemented
if (response.type == "tool_call") {
// intercept and interact with MCP server
} else if (response.type == "normal_text") {
// ...
}
Step 7
Cline calls the MCP server to execute
send_email(arguments)
Usually it’s via JSON-RPC but it can vary.
Step 8
MCP executes, runs the function.
Calls Gmail API (or whatever).
Step 9
Cline gets the result from MCP and sends result back to model.
Then see how AI model responses, the loop continues.
When do we need to create our own MCP
We build our own MCP only when we need reusable, structured access to real systems across multiple AI workflows.
Let’s again use sending an email as an example, but this time we want to send a monthly sales report with some pre-defined data and AI generated text.
This is the previous MCP
@mcp.tool()
def send_email(to: str, subject: str, body: str):
gmail_api.send(to, subject, body)
And we can implement the new MCP tool like this
@mcp.tool()
def get_monthly_sales_report(date):
sales_data_raw = database.sales.query(date)
sales_data = data_process(sales_data_raw) // critical business logic
return {
to: "leadership@company.com",
subject: "Monthly Sales Summary"
sales_data: sales_data
}
@mcp.tool()
def send_email(to: str, subject: str, body: str):
gmail_api.send(to, subject, body)
When we tell our AI agent that we want to send out the monthly sales summary
- The AI is told about the available tools, then decide to fetch the data first to help generating summary
- call
get_monthly_sales_reportto get data - Feed AI model with
sales_datato generate a summary in text - call
send_emailto send out the monthly report
With the get_monthly_sales_report implemented in our MCP, we made it reusable.
Without implementing the fixed procedure, the AI model will need to explore by itself, which can be really difficult and error prone even if we provide other tools like database_query, database_table_description, not to mention the business logic in data_process might even by critical, like removing user identification for privacy regulation.
We could even make it more fixed by
@mcp.tool()
def send_monthly_sales_report(date):
sales_data_raw = database.sales.query(date)
sales_data = data_process(sales_data_raw)
summary = generate_summary(sales_data) # some LLM
gmail_api.send(
to="leadership@company.com",
subject="Monthly Sales Summary",
body=summary
)
This does not rely on our local AI and agent to guess the flow, combining different tools.
Summary
What it is is the summary!