Model Context Protocol (MCP) is an open standard developed by Anthropic that defines how AI models connect to external tools and data sources. Think of it as USB-C for AI โ a universal connector that lets any AI application work with any tool or service through a common interface.
The Problem MCP Solves
Before MCP, every AI application needed custom integrations for each tool:
- App A implements Google Drive integration โ can't be reused by App B
- App B implements Slack integration โ different interface than App A's
- 100 AI apps ร 100 tools = 10,000 custom integrations
With MCP, tools expose a standard interface. AI apps implement the MCP client protocol once. Result: any MCP-compatible app works with any MCP-compatible tool โ the "N ร M problem" becomes "N + M."
MCP Architecture
MCP has three layers:
- MCP Host: The AI application (Claude Desktop, Claude Code, your custom app)
- MCP Client: Built into the host โ manages connections to servers
- MCP Server: Exposes tools, resources, and prompts via the MCP protocol
Communication happens over:
- stdio: Local servers โ MCP client spawns a subprocess and communicates via stdin/stdout
- SSE (Server-Sent Events): Remote servers โ HTTP with persistent event stream
MCP Primitives: Tools, Resources & Prompts
Tools
Functions the model can call with arguments. Like function calling โ but standardized and reusable across any MCP-compatible AI.
Resources
Data the model can read. Files, database rows, API responses โ exposed as URIs the model can access.
Prompts
Reusable prompt templates that users can invoke. Parameterized, versioned, and shared via the server.
Building an MCP Server in Python
# Install: pip install mcp
from mcp.server.fastmcp import FastMCP
from typing import Optional
import httpx
import json
# Create the MCP server
mcp = FastMCP("my-company-tools")
@mcp.tool()
async def get_customer_info(customer_id: str) -> str:
"""
Get detailed information about a customer by their ID.
Args:
customer_id: The unique customer identifier (e.g., 'CUST-12345')
Returns:
JSON string with customer name, email, plan, and account status
"""
# In production, query your actual database
# Here we simulate it
customers = {
"CUST-001": {
"name": "Alice Johnson",
"email": "alice@example.com",
"plan": "Enterprise",
"status": "active",
"mrr": 2499.00
}
}
customer = customers.get(customer_id)
if not customer:
return json.dumps({"error": f"Customer {customer_id} not found"})
return json.dumps(customer)
@mcp.tool()
async def search_knowledge_base(query: str, limit: int = 5) -> str:
"""
Search the company knowledge base for relevant articles.
Args:
query: Search query in natural language
limit: Maximum number of results to return (1-20)
"""
# Connect to your actual search backend
async with httpx.AsyncClient() as client:
response = await client.get(
"https://your-kb-api.internal/search",
params={"q": query, "limit": min(limit, 20)},
headers={"Authorization": "Bearer your-internal-token"}
)
results = response.json()
return json.dumps(results)
@mcp.tool()
async def create_support_ticket(
customer_id: str,
subject: str,
description: str,
priority: str = "normal"
) -> str:
"""
Create a support ticket for a customer.
Args:
customer_id: Customer identifier
subject: Brief subject line (max 100 chars)
description: Detailed description of the issue
priority: Ticket priority - 'low', 'normal', 'high', 'urgent'
"""
if priority not in ("low", "normal", "high", "urgent"):
return json.dumps({"error": "Invalid priority. Use: low, normal, high, urgent"})
# Create ticket in your ticketing system
ticket = {
"ticket_id": "TKT-98765",
"customer_id": customer_id,
"subject": subject,
"priority": priority,
"status": "open",
"created_at": "2026-05-30T10:00:00Z"
}
return json.dumps({"success": True, "ticket": ticket})
# Expose a resource (readable data)
@mcp.resource("config://company/support-policies")
async def get_support_policies() -> str:
"""Read-only access to support policies."""
return """
SUPPORT POLICIES (v2.1, updated 2026-05):
- Response SLA: 4h (normal), 1h (high), 15min (urgent)
- Refund policy: Full refund within 30 days, pro-rated after
- Escalation: VP of Support for enterprise customers
"""
# Run the server (stdio mode for Claude Desktop)
if __name__ == "__main__":
mcp.run(transport="stdio")
MCP Server Configuration
Configure Claude Desktop or Claude Code to use your server by adding it to the MCP config file:
# claude_desktop_config.json (Mac: ~/Library/Application Support/Claude/)
{
"mcpServers": {
"my-company-tools": {
"command": "python",
"args": ["/path/to/your/mcp_server.py"],
"env": {
"DATABASE_URL": "postgresql://...",
"API_KEY": "your-internal-api-key"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem",
"/Users/you/Documents"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..."
}
}
}
}
Using MCP from Your Own Application
# Connect to MCP servers from your Python application
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def use_mcp_tools():
server_params = StdioServerParameters(
command="python",
args=["mcp_server.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Initialize
await session.initialize()
# List available tools
tools = await session.list_tools()
print(f"Available tools: {[t.name for t in tools.tools]}")
# Call a tool
result = await session.call_tool(
"get_customer_info",
{"customer_id": "CUST-001"}
)
print(f"Tool result: {result.content[0].text}")
# Read a resource
resource = await session.read_resource(
"config://company/support-policies"
)
print(f"Resource: {resource.contents[0].text}")
asyncio.run(use_mcp_tools())
Popular Pre-Built MCP Servers
- filesystem โ read/write local files with access controls
- github โ search repos, read files, create issues and PRs
- postgres โ query PostgreSQL databases safely
- brave-search โ web and local search
- slack โ read channels, search messages, post replies
- google-drive โ read and search Drive files
- puppeteer โ browser automation for web interactions
Install via: npx -y @modelcontextprotocol/server-github
Key Takeaways
- MCP standardizes how AI models connect to tools โ write once, use with any MCP-compatible AI
- Three primitives: Tools (callable functions), Resources (readable data), Prompts (templates)
- FastMCP makes building Python MCP servers extremely simple โ just
@mcp.tool()decorators - Configure Claude Desktop/Code to use your servers via the JSON config file
- Dozens of pre-built MCP servers available for common services (GitHub, Slack, databases)
- MCP is Anthropic's open standard โ now supported by VS Code, Cursor, and growing ecosystem