By Sagar Shankaran, Founder of CallSphere
A comprehensive technical guide to Anthropic's Model Context Protocol -- the open standard for connecting AI models to external tools, data sources, and services. Covers architecture, server implementation, and real-world integration patterns.
Key takeaways
The Model Context Protocol (MCP) is an open standard created by Anthropic that defines how AI models connect to external tools, data sources, and services. Think of it as a USB-C port for AI -- a universal interface that lets any AI application talk to any data source or tool through a standardized protocol.
Before MCP, every AI application built its own bespoke integrations. Connecting Claude to a database required custom code. Connecting it to GitHub required different custom code. Connecting it to Slack required yet another integration. MCP replaces this M-times-N integration problem with a standardized protocol where each tool is implemented once as an MCP server and works with every MCP-compatible client.
MCP follows a client-server architecture with three components:
flowchart LR
HOST(["MCP host<br/>Claude Desktop or IDE"])
CLIENT["MCP client"]
subgraph SERVERS["MCP Servers"]
S1["Filesystem server"]
S2["GitHub server"]
S3["Postgres server"]
SX["Custom tool server"]
end
LLM["LLM session"]
OUT(["Grounded action"])
HOST <--> CLIENT
CLIENT <-->|stdio or HTTP+SSE| S1
CLIENT <--> S2
CLIENT <--> S3
CLIENT <--> SX
CLIENT --> LLM --> OUT
style HOST fill:#f1f5f9,stroke:#64748b,color:#0f172a
style CLIENT fill:#4f46e5,stroke:#4338ca,color:#fff
style OUT fill:#059669,stroke:#047857,color:#fff
The AI application that wants to use external tools. Claude Desktop, Claude Code, Cursor, and Windsurf are all MCP hosts. The host manages the user interface and LLM interaction.
A protocol client embedded in the host that maintains a connection to one or more MCP servers. The client handles capability negotiation, message routing, and lifecycle management.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
Lightweight services that expose specific capabilities through the MCP protocol. Each server provides one or more of three primitive types:
| Primitive | Description | Example |
|---|---|---|
| Tools | Functions the LLM can invoke | search_database, create_issue, send_email |
| Resources | Data the LLM can read | File contents, database records, API responses |
| Prompts | Pre-built prompt templates | Code review template, summarization template |
Host (Claude Desktop)
|
|-- MCP Client --> MCP Server (GitHub)
|-- MCP Client --> MCP Server (PostgreSQL)
|-- MCP Client --> MCP Server (Slack)
MCP servers can be implemented in Python or TypeScript using the official SDKs. Here is a complete Python example that exposes a database query tool:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import asyncpg
import json
server = Server("database-query")
# Connection pool (initialized on startup)
pool = None
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="query_database",
description="Execute a read-only SQL query against the production database. "
"Returns results as JSON. Only SELECT queries are allowed.",
inputSchema={
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "The SQL SELECT query to execute"
},
"limit": {
"type": "integer",
"description": "Max rows to return (default 100)",
"default": 100
}
},
"required": ["sql"]
}
),
Tool(
name="list_tables",
description="List all tables in the database with their column definitions.",
inputSchema={"type": "object", "properties": {}}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "query_database":
sql = arguments["sql"].strip()
if not sql.upper().startswith("SELECT"):
return [TextContent(
type="text",
text="Error: Only SELECT queries are allowed."
)]
limit = arguments.get("limit", 100)
sql_with_limit = f"{sql} LIMIT {limit}"
async with pool.acquire() as conn:
rows = await conn.fetch(sql_with_limit)
result = [dict(row) for row in rows]
return [TextContent(type="text", text=json.dumps(result, default=str))]
elif name == "list_tables":
async with pool.acquire() as conn:
tables = await conn.fetch("""
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position
""")
return [TextContent(type="text", text=json.dumps(
[dict(t) for t in tables], default=str
))]
async def main():
global pool
pool = await asyncpg.create_pool("postgresql://user:pass@localhost/mydb")
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{ name: "weather-service", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "get_weather",
description: "Get current weather for a city",
inputSchema: {
type: "object",
properties: {
city: { type: "string", description: "City name" },
},
required: ["city"],
},
},
],
}));
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "get_weather") {
const { city } = request.params.arguments;
const weather = await fetchWeatherAPI(city);
return {
content: [{ type: "text", text: JSON.stringify(weather) }],
};
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);
MCP servers are configured in claude_desktop_config.json:
{
"mcpServers": {
"database": {
"command": "python",
"args": ["/path/to/db_server.py"],
"env": {
"DATABASE_URL": "postgresql://user:pass@localhost/mydb"
}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxxx"
}
},
"filesystem": {
"command": "npx",
"args": [
"-y", "@modelcontextprotocol/server-filesystem",
"/Users/dev/projects"
]
}
}
}
MCP supports two transport mechanisms:
The default transport for local MCP servers. The host spawns the server as a child process and communicates via stdin/stdout. This is simple, secure (runs locally), and requires no network configuration.
For remote MCP servers that run on a different machine or as a cloud service. The client sends requests via HTTP POST and receives responses via an SSE stream.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route
transport = SseServerTransport("/messages")
async def handle_sse(request):
async with transport.connect_sse(
request.scope, request.receive, request._send
) as streams:
await server.run(streams[0], streams[1])
app = Starlette(routes=[
Route("/sse", endpoint=handle_sse),
Route("/messages", endpoint=transport.handle_post_message, methods=["POST"]),
])
The MCP ecosystem has grown rapidly since its November 2024 launch. As of January 2026, there are official servers for:
The community has contributed hundreds of additional servers. The MCP server registry at mcp.so lists over 500 community-built servers.
MCP servers have direct access to sensitive systems (databases, APIs, file systems). Security must be built in:
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
# Log every tool call
logger.info("tool_call", tool=name, args=arguments)
# Rate limiting
if not await rate_limiter.allow(name):
return [TextContent(type="text", text="Rate limit exceeded. Try again later.")]
# Input validation before execution
if name == "query_database":
sql = arguments.get("sql", "")
if any(keyword in sql.upper() for keyword in ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER"]):
return [TextContent(type="text", text="Error: Only read operations allowed.")]
# ... execute tool
MCP is complementary to, not a replacement for, LLM function calling. Function calling defines how the model decides to use tools within a single API call. MCP defines how those tools are discovered, connected, and managed across applications.
| Aspect | Function Calling | MCP |
|---|---|---|
| Scope | Single API call | Cross-application |
| Tool Discovery | Hardcoded in prompt | Dynamic via protocol |
| Implementation | In your app code | Separate server process |
| Reusability | Per-application | Any MCP host |
| Standardization | Provider-specific | Open standard |
For production deployments, MCP servers need the same reliability engineering as any microservice:
MCP has rapidly become the standard interface for AI tool integration. By investing in MCP server development, teams build reusable infrastructure that works across Claude, Claude Code, and the growing ecosystem of MCP-compatible applications.

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A founder's guide to the personal AI assistant market: best AI assistant apps, business-grade options, and how CallSphere's voice agent fits in.
A founder's guide to free AI agents, low-code AI agent builders, and how to know when you should pay for a real platform like CallSphere.
Using multiple chat AIs at once is a real 2026 workflow. Here is when it makes sense, how to set it up, and how CallSphere handles multi-model routing.
How to design a multi-agent system using MCP for tools and A2A for cross-vendor coordination, with a CallSphere voice agent as a participating node.
The 2026 desktop AI agent landscape — ServiceNow Project Arc, Anthropic Claude offerings, OpenAI agents, and Google Mariner. A buyer's map.
May 2026's biggest agent-architecture shift: planning, tool selection, and self-correction move inside the model. Framework code shrinks. Here is what changes.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI