Introduction
The Model Context Protocol (MCP) is an open standard that defines how AI models communicate with external tools and data sources. Introduced by Anthropic in late 2024, MCP aims to solve a fundamental problem in AI development: every integration between an LLM and an external system requires custom, bespoke work. MCP standardizes this communication, making it possible to build a tool once and use it with any MCP-compatible AI client.
This article explains what MCP is, why it matters, and how to build your own MCP server.
The Problem MCP Solves
Before MCP, connecting an AI model to external tools followed a fragmented pattern:
- OpenAI function calling used one format
- Anthropic tool use used another
- LangChain tools had their own interface
- Custom integrations were built from scratch each time
This meant that if you built a tool for OpenAI’s API, you couldn’t use it with Claude without rewriting the integration layer. Every new model required new adapters.
MCP addresses this by defining a standard protocol for tool communication. Build an MCP server once, and any MCP-compatible client can use it.
How MCP Works
MCP follows a client-server architecture:
MCP Server
An MCP server exposes capabilities to AI clients. A server can provide three types of capabilities:
- Tools: Functions the AI can call (e.g., search a database, send an email)
- Resources: Data the AI can read (e.g., file contents, database records)
- Prompts: Pre-defined prompt templates the AI can use
MCP Client
An MCP client (typically an AI application or agent framework) connects to MCP servers and makes their capabilities available to the AI model. The client handles the protocol communication, presenting tools to the model in the format it expects.
Protocol
MCP uses JSON-RPC 2.0 as its message format, communicating over stdio or HTTP with Server-Sent Events (SSE). This makes it language-agnostic and transport-flexible.
Building an MCP Server
Let’s build a practical MCP server that provides file system tools. We’ll use the official MCP Python SDK.
Installation
pip install mcp
A File System MCP Server
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import os
import json
server = Server("filesystem-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="read_file",
description="Read the contents of a file",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Path to the file to read"
}
},
"required": ["path"]
}
),
Tool(
name="list_directory",
description="List files in a directory",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Path to the directory to list"
}
},
"required": ["path"]
}
),
Tool(
name="write_file",
description="Write content to a file",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Path to the file to write"
},
"content": {
"type": "string",
"description": "Content to write"
}
},
"required": ["path", "content"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "read_file":
path = arguments["path"]
with open(path, "r") as f:
content = f.read()
return [TextContent(type="text", text=content)]
elif name == "list_directory":
path = arguments["path"]
entries = os.listdir(path)
result = "\n".join(entries)
return [TextContent(type="text", text=result)]
elif name == "write_file":
path = arguments["path"]
content = arguments["content"]
with open(path, "w") as f:
f.write(content)
return [TextContent(type="text", text=f"Written to {path}")]
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Connecting from a Client
Once your server is running, MCP-compatible clients can connect to it. For example, in Claude Desktop, you configure MCP servers in the settings:
{
"mcpServers": {
"filesystem": {
"command": "python",
"args": ["/path/to/your/server.py"]
}
}
}
The AI model can then use your tools directly, with the MCP client handling all the communication.
Practical Use Cases
Database Query Tool
An MCP server that lets AI models query your database:
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="query_database",
description="Execute a read-only SQL query",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "SQL SELECT query"
}
},
"required": ["query"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "query_database":
import sqlite3
conn = sqlite3.connect("app.db")
cursor = conn.execute(arguments["query"])
columns = [desc[0] for desc in cursor.description]
rows = cursor.fetchall()
conn.close()
results = [dict(zip(columns, row)) for row in rows]
return [TextContent(type="text", text=json.dumps(results, indent=2))]
API Integration Server
Expose your internal APIs to AI models through MCP:
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="get_customer",
description="Get customer details by ID",
inputSchema={
"type": "object",
"properties": {
"customer_id": {"type": "string"}
},
"required": ["customer_id"]
}
),
Tool(
name="create_ticket",
description="Create a support ticket",
inputSchema={
"type": "object",
"properties": {
"customer_id": {"type": "string"},
"subject": {"type": "string"},
"description": {"type": "string"}
},
"required": ["customer_id", "subject", "description"]
}
)
]
Why MCP Matters
For Developers
- Build once, use everywhere: An MCP server works with any MCP-compatible client
- Standardized interface: No more writing custom adapters for each AI provider
- Composability: Multiple MCP servers can run simultaneously, each providing different capabilities
For Organizations
- Security: MCP servers run locally or on your infrastructure, keeping data in your control
- Reusability: Build internal tool servers once and share them across teams
- Ecosystem: As more tools are built as MCP servers, the value of the ecosystem grows
Current Ecosystem
MCP is still early, but adoption is growing:
- Claude Desktop supports MCP servers natively
- Open-source MCP servers exist for popular services like GitHub, Slack, and Google Drive
- Community servers are being published for databases, file systems, and APIs
- Agent frameworks are adding MCP client support
Conclusion
The Model Context Protocol represents a meaningful step toward standardizing how AI models interact with the external world. By providing a common protocol for tool communication, MCP reduces the integration burden and makes AI tools more portable and composable.
Building an MCP server is straightforward, and the payoff is significant: any MCP-compatible AI client can use your tools without custom integration work. As the ecosystem grows, MCP servers will become the standard way to expose capabilities to AI models, much as REST APIs became the standard for web services.