Summary
What this post covers: A comprehensive examination of the Model Context Protocol (MCP), including its architecture, the three primitives (tools, resources and prompts), transport mechanics, server construction in Python and TypeScript, and the protocol’s effect on the AI integration landscape.
Key insights:
- MCP addresses the N times M integration problem that has long affected AI tooling. Rather than every AI application constructing a custom connector for every tool, a single MCP server is compatible with every MCP-aware client, including Claude Desktop, Claude Code, Cursor, VS Code, Zed and Windsurf.
- The protocol exposes three primitives: tools (model-invoked actions), resources (application-controlled context) and prompts (user-triggered templates). The distinction between who controls each primitive is what enables the design to scale.
- The transport layer separates stdio, which is best suited to local subprocesses, trust boundaries and development, from streamable HTTP, which supports remote servers with OAuth. The correct selection is important for both security and latency.
- Production MCP servers should validate inputs against JSON Schema, return structured errors, scope OAuth tokens narrowly, and guard against prompt-injection attacks in which untrusted resource content attempts to hijack tool calls.
- MCP is becoming for AI what HTTP became for the web. Anthropic open-sourced the protocol from the outset, and the ecosystem now includes official servers for GitHub, Slack, Postgres, Filesystem and Puppeteer, alongside hundreds of community connectors.
Main topics: What Is MCP?, The Architecture of MCP, The Three Primitives: Tools, Resources, and Prompts, Transport Layer: How MCP Communicates, Building a First MCP Server: A Complete Tutorial, Popular MCP Servers and the Ecosystem, MCP in Claude Code: A Detailed Examination, MCP vs Other Approaches, Security Considerations, Building Production MCP Servers, The Future of MCP, Getting Started: Your Next Steps, Final Thoughts, References.
Consider an exceptionally capable assistant able to analyse data, write code and answer complex questions, but confined to a windowless room with no telephone, no internet connection and no access to any of the user’s files. Each time the user requires the assistant to check email, the user must print the messages, deliver them to the room, slide them under the door, wait for a response, and then carry the reply back. Multiplying this process across every tool in use, including calendar, database, project management system and cloud infrastructure, describes the state of AI integrations before the Model Context Protocol. The arrangement is as inefficient as it sounds.
Before MCP, every AI application had to build a custom integration for every data source and tool it sought to access. Allowing Claude to read Google Drive required a custom integration. Permitting database queries required another. Connecting to Slack required a further one. Every AI company and every tool vendor had to negotiate, build and maintain a unique connector. The arithmetic was unforgiving: N AI applications multiplied by M tools produced N times M custom integrations, each with its own authentication flow, data format and failure modes.
The situation resembled the early internet before HTTP. Each system used its own method of transferring documents between computers, and none communicated with the others. HTTP then introduced a single standard for requesting and serving documents, and the web expanded rapidly.
MCP performs the same function for AI. Announced by Anthropic in late 2024 and open-sourced from the outset, the Model Context Protocol is a universal standard that allows any AI model to connect to any tool or data source through a single, well-defined protocol. An MCP server constructed once can immediately be used by any MCP-compatible AI application, including Claude Desktop, Claude Code, VS Code Copilot, Cursor, Windsurf and Zed. No custom integrations are required and no vendor lock-in is introduced.
The remainder of this article presents a comprehensive examination of MCP. It explains the architecture, the three core primitives and the operation of the transport layer, and walks through the construction of MCP servers in both Python and TypeScript.
What Is MCP?
The Model Context Protocol (MCP) is an open standard for communication between AI applications, referred to as clients or hosts, and external data sources and tools, referred to as servers. It functions as a universal language that AI models and tools can use to understand one another, regardless of which entity built them.
The USB Analogy
The clearest way to understand MCP is through an analogy with USB. Before USB, every peripheral, including printers, scanners, keyboards and cameras, used its own proprietary cable and connector. Each desk became a tangle of incompatible cables, and purchasing a new device required confirming that the correct port was supported. USB introduced a single connector and a single protocol covering every device. USB-C extended this further by carrying charging, data, video and audio over a single cable across laptops, phones, tablets and monitors.
MCP is the USB-C of AI integrations. A single standard connector serves every purpose. A GitHub MCP server functions with Claude, with Cursor, with VS Code Copilot and with any future AI application that implements the MCP client specification. The server is built once and used in every context.
Who Created It and Why
MCP was created by Anthropic and open-sourced under a permissive licence. The specification, SDKs and reference implementations are publicly available on GitHub. Anthropic did not develop MCP in order to lock developers into Claude. The protocol was developed because the N times M integration problem was constraining the entire AI industry.
The arithmetic is straightforward. Suppose there are 10 AI applications and 50 tools. Without a standard protocol, 10 multiplied by 50 produces 500 custom integrations. Each integration must be built, tested, documented and maintained. Adding one further AI application then requires 50 additional integrations, and adding one further tool requires 10 additional integrations. The problem scales poorly.
With MCP, each AI application implements a single MCP client, and each tool implements a single MCP server. The total becomes 10 plus 50, or 60 implementations. Adding a new AI application requires one further client. Adding a new tool requires one further server. The problem becomes linear rather than multiplicative.
What MCP Is Not
To avoid confusion, the following clarifications regarding what MCP is not are useful.
- MCP is not an API. It is a protocol specification, in the same category as HTTP or WebSocket. APIs are constructed on top of protocols.
- MCP is not a framework. It is not LangChain, CrewAI or AutoGen. Frameworks provide opinionated structures for building applications. MCP provides a communication standard.
- MCP is not a library. Although SDKs exist for Python and TypeScript, the protocol itself is language-agnostic. It can be implemented in Rust, Go, Java or any language capable of handling JSON-RPC.
- MCP is not Anthropic-only. It is an open standard. Microsoft, Google and many open-source projects are adopting it.
The closest analogy in software engineering is the Language Server Protocol (LSP), developed by Microsoft for VS Code. LSP standardised how code editors communicate with language-specific intelligence servers responsible for autocomplete, go-to-definition and error checking. Before LSP, every editor required a dedicated plugin for every language. After LSP, a single language server functions with any editor. MCP performs the same role for AI models connecting to tools and data.
Current Adoption
As of early 2026, MCP has been adopted by a rapidly growing set of applications and platforms.
| Application | Type | MCP Support |
|---|---|---|
| Claude Desktop | AI Assistant | Full (host + client) |
| Claude Code | CLI Agent | Full (host + client) |
| VS Code (GitHub Copilot) | IDE | MCP server support |
| Cursor | AI IDE | Full MCP support |
| Windsurf | AI IDE | Full MCP support |
| Zed | Code Editor | MCP integration |
| Sourcegraph Cody | Code AI | MCP server support |
The Architecture of MCP
MCP follows a client-server architecture composed of three distinct components. Understanding how these components fit together is essential before examining the primitives and transport layers in detail.
Three Core Components
The architecture is structured as follows.
┌─────────────────────────────────────────────────────┐
│ MCP HOST │
│ (e.g., Claude Desktop) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ MCP │ │ MCP │ │ MCP │ │
│ │ Client 1 │ │ Client 2 │ │ Client 3 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼──────────────┼──────────────┼────────────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ MCP │ │ MCP │ │ MCP │
│ Server A │ │ Server B │ │ Server C │
│ (GitHub) │ │ (DB) │ │ (Slack) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ GitHub │ │ PostgreSQL│ │ Slack │
│ API │ │ Database │ │ API │
└──────────┘ └──────────┘ └──────────┘
MCP Hosts are the AI applications that require access to external tools and data. Claude Desktop, Claude Code, Cursor and any custom AI application a developer may build can serve as an MCP host. The host is responsible for managing the user interface, running the AI model, and coordinating connections to one or more MCP servers. In the HTTP analogy, the host corresponds to a web browser: it is the application with which the user interacts, and it knows how to speak the protocol to complete tasks.
MCP Clients are protocol-level connectors that reside within hosts. Each client maintains a one-to-one connection with a specific MCP server. A Claude Desktop installation connected to three MCP servers (GitHub, a database and Slack) runs three MCP clients internally. The client handles low-level communication, including sending JSON-RPC messages, negotiating capabilities and managing the connection lifecycle. Developers typically do not build clients directly, since the host application provides them.
MCP Servers are the services that expose tools, resources and prompts to AI applications. A GitHub MCP server may expose tools such as create_issue, search_repos and list_pull_requests. A database MCP server may expose tools such as run_query and list_tables. Each server exposes its capabilities through a standard interface, and any MCP client can discover and use them. In the HTTP analogy, MCP servers correspond to web servers: they serve content and functionality to any client capable of speaking the protocol.
MCP servers may run locally on a developer’s machine using the stdio transport, in which case they operate as a subprocess, or remotely as a web service using the HTTP+SSE transport. This flexibility means that a developer can begin with a simple local server for personal use and later deploy it as a shared service for an entire team.
How It Differs from Traditional API Integrations
In a traditional integration, the AI application calls an external API directly. The developer writes HTTP requests, handles authentication, parses responses and manages errors, all in custom code embedded in the application. When the API changes, the developer updates the code. When a new AI application must be supported, the integration is rewritten.
With MCP, an abstraction layer sits between the application and the underlying service. The AI application neither knows nor needs to know how the MCP server communicates with GitHub, Slack or a particular database. It is only required to speak MCP. The server handles all API-specific logic. The implications of this separation of concerns are as follows.
- AI applications can support new tools without code changes, since the host need only be pointed at a new MCP server.
- Tool providers can update their APIs without disrupting AI integrations, since only the MCP server requires modification.
- The AI model can discover available tools dynamically at runtime through the standard capability-negotiation mechanism.
The Three Primitives: Tools, Resources, and Prompts
MCP defines three core primitives, that is, three categories of capability that servers may expose to clients. Each serves a different purpose and is controlled by a different party. Understanding these primitives is essential to understanding MCP.
Tools (Model-Controlled)
Tools are functions that the AI model can invoke to perform actions. They are the most commonly used primitive and the first that practitioners associate with MCP. Tools allow the model to search files, run database queries, send messages, create GitHub issues, deploy code and perform any other operation that can be expressed as a function call.
Each tool is defined by a name, a description (which the model reads to determine when the tool should be used) and an input schema in JSON Schema format. When the model determines that a tool is required in order to answer the user’s question, it generates the appropriate arguments, the MCP client sends the call to the server, the server executes the function, and the result returns to the model.
A complete example of a tool definition is shown below.
{
"name": "query_database",
"description": "Execute a read-only SQL query against the application database. Use this tool when the user asks about data stored in our systems — customer counts, order history, revenue figures, etc. Only SELECT queries are allowed.",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The SQL SELECT query to execute"
},
"database": {
"type": "string",
"enum": ["production", "analytics", "staging"],
"description": "Which database to query"
},
"limit": {
"type": "integer",
"default": 100,
"description": "Maximum number of rows to return"
}
},
"required": ["query", "database"]
}
}
The central point to understand is that tools are model-controlled. The AI model determines when to invoke a tool based on the user’s intent. When the user asks “how many customers signed up last month?”, the model determines that it must call query_database in order to answer. The model generates the SQL, selects the database and issues the call. The concept is the same as function calling or tool calling in the Claude and OpenAI APIs, but standardised across all MCP-compatible applications.
Resources (Application-Controlled)
Resources are data that the application can expose to the AI model. If tools resemble POST endpoints in REST in that they perform actions, resources resemble GET endpoints in that they supply data. Resources provide the model with context, including background information, file contents, configuration and documentation, that helps it understand the user’s situation and generate higher-quality responses.
Resources are identified by URIs in the same manner as web pages. A file system MCP server might expose resources such as file:///home/user/project/README.md. A database server might expose db://users/123 to represent a specific user record. A project management server might expose jira://PROJECT-456 for a specific ticket.
An example of a resource definition is shown below.
{
"uri": "docs://api/authentication",
"name": "Authentication API Documentation",
"description": "Complete documentation for the authentication API, including endpoints, request/response formats, and error codes",
"mimeType": "text/markdown"
}
Resources are application-controlled rather than model-controlled. The host application determines when to fetch and present resources to the model. When a user opens a project in Claude Code, for example, the application may automatically fetch the project’s README and configuration files as resources, supplying the model with context before any question is asked. Resources can also be dynamic, since a server may support subscriptions that notify the client when a resource changes.
Prompts (User-Controlled)
Prompts are pre-built prompt templates that servers may expose. They provide users, or applications, with rapid access to common workflows without requiring the full instructions to be typed each time. A code review MCP server might expose a /review-code prompt containing a detailed template for analysing code quality, security and performance. A documentation server might expose a /summarize prompt optimised for generating concise summaries.
An example of a prompt definition is shown below.
{
"name": "review-code",
"description": "Perform a thorough code review with focus on bugs, security, performance, and maintainability",
"arguments": [
{
"name": "code",
"description": "The code to review",
"required": true
},
{
"name": "language",
"description": "Programming language of the code",
"required": false
},
{
"name": "focus",
"description": "Specific area to focus on (security, performance, readability)",
"required": false
}
]
}
Prompts are user-controlled. The user explicitly selects a prompt from the available list, supplies any required arguments, and the expanded prompt is sent to the model. This differs from tools, where the model decides, and from resources, where the application decides.
Comparison Table
| Aspect | Tools | Resources | Prompts |
|---|---|---|---|
| Controlled by | AI Model | Application | User |
| Direction | Model → Server (action) | Server → Model (data) | Server → User (template) |
| REST analogy | POST endpoints | GET endpoints | Pre-built query templates |
| Example | create_issue, run_query | file contents, DB records | /review-code, /summarize |
| Discovery | tools/list | resources/list | prompts/list |
| Use case | Perform actions | Provide context | Templated workflows |
Transport Layer: How MCP Communicates
The protocol requires a mechanism for transmitting messages between clients and servers. MCP supports two transport mechanisms, each suited to different deployment scenarios.
stdio (Standard I/O) Transport
The stdio transport is the simplest and most common way to run MCP servers. The host application launches the MCP server as a subprocess on the same machine, and the two communicate via standard input (stdin) and standard output (stdout). Messages are JSON-RPC 2.0 objects, sent as newline-delimited JSON.
The sequence of events when a stdio MCP server is configured in Claude Desktop is as follows.
- The server configuration is added to
claude_desktop_config.json. - Claude Desktop launches the server process, for example
python weather_server.py. - The client sends an
initializerequest over stdin. - The server responds with its capabilities, including the tools, resources and prompts it offers.
- The client sends a
tools/listrequest to discover available tools. - When the model wishes to invoke a tool, the client sends a
tools/callrequest over stdin. - The server executes the tool and returns the result over stdout.
The stdio transport is well suited to local development, personal tools and single-user scenarios. It requires no network configuration, no authentication setup and no supporting infrastructure. Only the server script on the local machine is required.
HTTP + Server-Sent Events (SSE) Transport
For remote servers, shared team tools and production deployments, MCP supports HTTP with Server-Sent Events. The client connects to the server over HTTP, sends requests as HTTP POST messages, and receives responses and notifications via an SSE stream.
This transport enables scenarios that stdio cannot accommodate.
- Remote access: the server runs on a different machine, in the cloud, or behind a load balancer.
- Multi-user operation: multiple clients may connect to the same server simultaneously.
- Authentication: standard HTTP authentication mechanisms, including Bearer tokens and OAuth, may be used.
- Monitoring: standard HTTP logging, metrics and tracing tools function by default.
- Scalability: the server can be deployed as a containerised service with horizontal scaling.
Transport Comparison
| Aspect | stdio | HTTP + SSE |
|---|---|---|
| Setup complexity | Minimal; only requires running a script | Moderate—needs web server |
| Best for | Local development and personal tools | Remote, shared and production deployments |
| Authentication | OS-level (file permissions) | HTTP auth (tokens, OAuth) |
| Scalability | Single user, single machine | Multi-user, load balanced |
| Debugging | Read stdout/stderr | HTTP logs, network tools |
| Network required | No | Yes |
Building a First MCP Server: A Complete Tutorial
Theory is useful, but practical construction provides a clearer understanding. This section walks through two complete, runnable MCP servers, one in Python and one in TypeScript. Both are fully functional and ready to connect to Claude Desktop or Claude Code.
Python MCP Server: Weather Service
Step 1: Install dependencies
# Create a new project directory
mkdir mcp-weather-server && cd mcp-weather-server
# Initialize with uv (recommended) or pip
uv init
uv add mcp httpx
# Or with pip
pip install mcp httpx
Step 2: Create the server
Create a file called weather_server.py:
"""MCP Weather Server — exposes weather tools, resources, and prompts."""
import json
import httpx
from mcp.server.fastmcp import FastMCP
# Create the MCP server
mcp = FastMCP("weather-service")
# --- TOOLS (Model-Controlled) ---
@mcp.tool()
async def get_weather(city: str, units: str = "celsius") -> str:
"""Get the current weather for a city.
Use this tool when the user asks about weather conditions,
temperature, or forecasts for a specific location.
Args:
city: The city name (e.g., "Tokyo", "New York", "London")
units: Temperature units — "celsius" or "fahrenheit"
"""
# Using the free Open-Meteo API (no API key required)
# First, geocode the city name
async with httpx.AsyncClient() as client:
geo_response = await client.get(
"https://geocoding-api.open-meteo.com/v1/search",
params={"name": city, "count": 1}
)
geo_data = geo_response.json()
if "results" not in geo_data:
return f"Could not find location: {city}"
location = geo_data["results"][0]
lat = location["latitude"]
lon = location["longitude"]
name = location["name"]
country = location.get("country", "")
# Fetch weather data
temp_unit = "fahrenheit" if units == "fahrenheit" else "celsius"
weather_response = await client.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": lat,
"longitude": lon,
"current": "temperature_2m,wind_speed_10m,relative_humidity_2m,weather_code",
"temperature_unit": temp_unit,
}
)
weather = weather_response.json()["current"]
unit_symbol = "°F" if units == "fahrenheit" else "°C"
return (
f"Weather in {name}, {country}:\n"
f"Temperature: {weather['temperature_2m']}{unit_symbol}\n"
f"Humidity: {weather['relative_humidity_2m']}%\n"
f"Wind Speed: {weather['wind_speed_10m']} km/h\n"
f"Conditions: Weather code {weather['weather_code']}"
)
@mcp.tool()
async def get_forecast(city: str, days: int = 3) -> str:
"""Get a multi-day weather forecast for a city.
Args:
city: The city name
days: Number of days to forecast (1-7)
"""
days = min(max(days, 1), 7)
async with httpx.AsyncClient() as client:
geo_response = await client.get(
"https://geocoding-api.open-meteo.com/v1/search",
params={"name": city, "count": 1}
)
geo_data = geo_response.json()
if "results" not in geo_data:
return f"Could not find location: {city}"
location = geo_data["results"][0]
weather_response = await client.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": location["latitude"],
"longitude": location["longitude"],
"daily": "temperature_2m_max,temperature_2m_min,weather_code",
"forecast_days": days,
}
)
daily = weather_response.json()["daily"]
lines = [f"Forecast for {location['name']}:"]
for i in range(days):
lines.append(
f" {daily['time'][i]}: "
f"{daily['temperature_2m_min'][i]}°C — "
f"{daily['temperature_2m_max'][i]}°C "
f"(code: {daily['weather_code'][i]})"
)
return "\n".join(lines)
# --- RESOURCES (Application-Controlled) ---
@mcp.resource("weather://supported-cities")
async def list_supported_cities() -> str:
"""List of major cities with reliable weather data."""
cities = [
"Tokyo", "New York", "London", "Paris", "Sydney",
"Berlin", "Toronto", "Singapore", "Dubai", "Seoul",
"San Francisco", "Mumbai", "São Paulo", "Cairo", "Bangkok"
]
return json.dumps({"cities": cities, "note": "Any city works, these are examples"})
# --- PROMPTS (User-Controlled) ---
@mcp.prompt()
def weather_report(city: str) -> str:
"""Generate a detailed weather report for a city."""
return f"""Please provide a comprehensive weather report for {city}.
Include:
1. Current conditions (temperature, humidity, wind)
2. A {3}-day forecast
3. What to wear and any weather advisories
4. Best time of day for outdoor activities
Use the get_weather and get_forecast tools to gather the data,
then present it in a clear, friendly format."""
if __name__ == "__main__":
mcp.run(transport="stdio")
The above constitutes a complete, runnable MCP server in approximately 80 lines of meaningful code. It exposes two tools (get_weather and get_forecast), one resource (weather://supported-cities) and one prompt (weather_report).
FastMCP class from the mcp package is the high-level API that handles JSON-RPC boilerplate, capability negotiation and message routing on behalf of the developer. The decorators @mcp.tool(), @mcp.resource() and @mcp.prompt() map directly to the three MCP primitives.
TypeScript MCP Server: Database Query Service
Step 1: Setup
# Create project
mkdir mcp-database-server && cd mcp-database-server
npm init -y
npm install @modelcontextprotocol/sdk better-sqlite3
npm install -D typescript @types/better-sqlite3 @types/node
npx tsc --init
Step 2: Create the server
Create src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import Database from "better-sqlite3";
import { z } from "zod";
// Open (or create) a SQLite database
const db = new Database("./data.db");
// Create a sample table for demonstration
db.exec(`
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
category TEXT,
price REAL,
stock INTEGER
)
`);
// Insert sample data if empty
const count = db.prepare("SELECT COUNT(*) as c FROM products").get() as any;
if (count.c === 0) {
const insert = db.prepare(
"INSERT INTO products (name, category, price, stock) VALUES (?, ?, ?, ?)"
);
const products = [
["Mechanical Keyboard", "Electronics", 149.99, 50],
["Ergonomic Mouse", "Electronics", 79.99, 120],
["4K Monitor", "Electronics", 599.99, 30],
["Standing Desk", "Furniture", 449.99, 15],
["Desk Lamp", "Furniture", 39.99, 200],
];
for (const p of products) {
insert.run(...p);
}
}
// Create the MCP server
const server = new McpServer({
name: "database-query",
version: "1.0.0",
});
// --- TOOLS ---
server.tool(
"query",
"Execute a read-only SQL query against the database. Only SELECT statements are allowed. Use this when the user asks about products, inventory, or any data in the database.",
{
sql: z.string().describe("The SQL SELECT query to execute"),
},
async ({ sql }) => {
// Security: only allow SELECT queries
const trimmed = sql.trim().toUpperCase();
if (!trimmed.startsWith("SELECT")) {
return {
content: [
{ type: "text", text: "Error: Only SELECT queries are allowed." },
],
};
}
try {
const rows = db.prepare(sql).all();
return {
content: [
{
type: "text",
text: JSON.stringify(rows, null, 2),
},
],
};
} catch (error: any) {
return {
content: [
{ type: "text", text: `Query error: ${error.message}` },
],
};
}
}
);
server.tool(
"list_tables",
"List all tables in the database with their schemas.",
{},
async () => {
const tables = db
.prepare(
"SELECT name, sql FROM sqlite_master WHERE type='table' ORDER BY name"
)
.all();
return {
content: [
{
type: "text",
text: JSON.stringify(tables, null, 2),
},
],
};
}
);
server.tool(
"describe_table",
"Get the column information for a specific table.",
{
table_name: z.string().describe("Name of the table to describe"),
},
async ({ table_name }) => {
try {
const columns = db.prepare(`PRAGMA table_info(${table_name})`).all();
return {
content: [
{
type: "text",
text: JSON.stringify(columns, null, 2),
},
],
};
} catch (error: any) {
return {
content: [
{ type: "text", text: `Error: ${error.message}` },
],
};
}
}
);
// --- Start the server ---
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Database MCP server running on stdio");
}
main().catch(console.error);
This TypeScript server exposes three tools for interacting with a SQLite database: query, which executes SELECT statements; list_tables, which discovers the schema; and describe_table, which inspects column details. It includes a security check that prevents non-SELECT queries from executing.
Step 3: Connect to Claude Desktop
To use an MCP server with Claude Desktop, the configuration file must be edited. On macOS it is located at ~/Library/Application Support/Claude/claude_desktop_config.json. On Windows it is located at %APPDATA%\Claude\claude_desktop_config.json.
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/absolute/path/to/weather_server.py"]
},
"database": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"]
}
}
}
After saving the configuration and restarting Claude Desktop, the MCP tools icon appears in the chat interface. Claude then has access to the weather and database tools. The user may ask, for example, “What is the weather in Tokyo?” or “Show me all products in the database.” Claude will discover the appropriate tools, invoke them and present the results in natural language.
Step 4: Connect to Claude Code
For Claude Code, MCP servers are added to the project-level settings file at .claude/settings.json.
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/absolute/path/to/weather_server.py"]
}
}
}
Servers may alternatively be added at the user level in ~/.claude/settings.json so that they are available across all projects. Claude Code automatically discovers the tools at startup, and they are available in conversations in the same manner as the built-in tools.
Popular MCP Servers and the Ecosystem
One of the most notable aspects of MCP is the rapidly growing ecosystem of pre-built servers. Building every connector from scratch is unnecessary, as servers already exist for the most popular tools and services.
Official and Reference Servers
Anthropic and the MCP community maintain a collection of reference servers covering common use cases.
| Server | What It Does | Transport | Source |
|---|---|---|---|
| Filesystem | Read, write, search files on disk | stdio | Official |
| GitHub | Repos, issues, PRs, commits, actions | stdio | Official |
| GitLab | Projects, merge requests, pipelines | stdio | Official |
| Google Drive | Search, read files from Drive | stdio | Official |
| Slack | Channels, messages, users | stdio | Official |
| PostgreSQL | Query databases, inspect schemas | stdio | Official |
| SQLite | Query and manage SQLite databases | stdio | Official |
| Brave Search | Web and local search via Brave | stdio | Official |
| Puppeteer | Browser automation, screenshots | stdio | Official |
| Notion | Pages, databases, search | stdio | Community |
| Linear | Issues, projects, teams | stdio | Community |
| Docker | Container management, images, logs | stdio | Community |
| Kubernetes | Cluster management, pods, services | stdio / HTTP | Community |
| Stripe | Payments, customers, subscriptions | stdio | Community |
| AWS | S3, Lambda, CloudWatch, EC2 | stdio | Community |
Discovering MCP Servers
Several directories and registries have emerged to assist in locating MCP servers.
- Smithery (smithery.ai): a curated registry of MCP servers with installation instructions and ratings.
- MCP Hub: a community-maintained directory with categories and search functionality.
- awesome-mcp-servers on GitHub: a curated list in the awesome-list tradition, organised by category.
- npm and PyPI: many MCP servers are published as packages installable via
npm installorpip install.
MCP in Claude Code: A Detailed Examination
Claude Code is the context in which MCP is particularly relevant for developers. Claude Code is itself an MCP host, and its built-in capabilities, including Read, Write, Edit, Bash, Grep and Glob, are essentially MCP tools internally.
Built-In Tools as MCP
When Claude Code reads a file, edits code or runs a shell command, it uses the same tool-calling pattern that MCP standardises. The difference is that these tools are built directly into the Claude Code host rather than running as external MCP servers. The conceptual model is identical: the AI model sees a list of available tools with descriptions and schemas, determines which to invoke, generates the arguments and processes the result.
Claude Code was therefore designed from the outset to be extensible via MCP. Additional capabilities can be added to Claude Code simply by directing it to an MCP server.
Adding Custom MCP Servers
Two levels of MCP configuration exist in Claude Code.
Project-level configuration resides in .claude/settings.json within the project.
{
"mcpServers": {
"project-db": {
"command": "python",
"args": ["./tools/db_server.py"],
"env": {
"DATABASE_URL": "postgresql://localhost:5432/myapp"
}
}
}
}
Project-level servers are only available when work is conducted in that specific project. This level is appropriate for project-specific tools such as database access, deployment scripts and custom linters.
User-level configuration resides in ~/.claude/settings.json.
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..."
}
},
"slack": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-server-slack"],
"env": {
"SLACK_BOT_TOKEN": "xoxb-..."
}
}
}
}
User-level servers are available in every project. This level is appropriate for universal tools such as GitHub, Slack and Notion that are used across all work.
A Realistic Workflow Example
Consider a developer who has Claude Code configured with GitHub, Notion and Slack MCP servers. A representative workflow is as follows.
- The developer instructs Claude Code: “Check the latest bug reports in our GitHub repository, summarise them in a Notion page, and post a summary to the #engineering Slack channel.”
- Claude Code uses the GitHub MCP server to call
list_issueswith labels=[“bug”] and state=”open”. - It reads each issue’s details using
get_issue. - It calls the Notion MCP server’s
create_pagetool with a structured summary. - It calls the Slack MCP server’s
send_messagetool to post to #engineering. - All of this occurs in a single conversation, using standard MCP tools, with no custom code.
This illustrates the value of MCP. Each server was constructed independently, potentially by different teams or open-source contributors. Because they all speak the same protocol, Claude Code can orchestrate them without friction.
MCP vs Other Approaches
MCP did not emerge in a vacuum. Several other approaches exist for connecting AI models with external tools. Understanding how MCP compares to these alternatives supports informed architectural decisions.
MCP vs OpenAI Function Calling
OpenAI’s function calling, alongside Anthropic’s tool use, allows developers to define tools in API calls and have the model generate structured arguments. The feature is powerful but provider-specific and requires custom integration code for each tool.
With function calling, the tool definitions and execution logic reside in application code. A GitHub integration built for an OpenAI-powered application cannot be reused in a Claude-powered application without rewriting it. The function definitions may appear similar, but the supporting code, including authentication, error handling and response formatting, is embedded in each application.
MCP separates tool definition and execution into a standalone server. A GitHub MCP server constructed once functions with any MCP host. The tool definitions travel with the server rather than with the application.
MCP vs OpenAI Plugins (Deprecated)
OpenAI Plugins, launched in 2023 and later deprecated, were an earlier attempt to address the same problem. Plugins used OpenAPI specifications to describe available endpoints, which ChatGPT could call. Plugins were, however, OpenAI-only, required the hosting of a public API endpoint with an OpenAPI specification, and presented significant security and reliability issues. MCP addresses each of these limitations: it is an open standard, it supports local servers without requiring public endpoints, and it provides a more robust security model.
MCP vs LangChain Tools
LangChain provides a framework for building AI applications, including a tool abstraction. LangChain tools are Python or JavaScript functions decorated with metadata. They are useful within the LangChain ecosystem but are framework-specific: a LangChain tool cannot be used outside LangChain without extracting the underlying logic.
MCP tools run as independent servers to which any MCP client may connect. They are language-agnostic, framework-agnostic and transport-agnostic. A Python MCP server functions with a TypeScript MCP client. A LangChain tool functions only within LangChain.
That said, LangChain has begun adding MCP integration, allowing MCP servers to be used as LangChain tools. The two approaches are converging rather than competing.
MCP vs Custom REST APIs
A natural question is why the AI model does not simply call REST APIs directly. The answer is that REST APIs were designed for machine-to-machine communication between known systems. They assume the developer knows the endpoint URL, the request format and the authentication method in advance. No standard discovery mechanism exists: documentation must be read and client code must be written.
MCP adds a discovery and negotiation layer. When an MCP client connects to a server, it automatically discovers the available tools, resources and prompts, together with their schemas. The AI model can then decide which tools to use on the basis of the descriptions. No custom client code is required.
Detailed Comparison Table
| Feature | MCP | Function Calling | LangChain | REST APIs |
|---|---|---|---|---|
| Type | Protocol | API Feature | Framework | Architecture |
| Provider lock-in | None | High | Framework | None |
| Tool discovery | Automatic | Manual | Automatic | Manual |
| Language support | Any | Any | Python / JS | Any |
| Reusability | Build once, use everywhere | Per application | Within framework | Custom clients |
| Resources support | Yes | No | No (separate) | Yes (GET) |
| Prompt templates | Yes | No | Yes | No |
| Local execution | stdio transport | In-process | In-process | Needs server |
Security Considerations
Connecting AI models to tools and data is a powerful capability, and that power carries responsibility. MCP includes several security mechanisms, and understanding them is essential to building production-ready servers.
Tool Authorisation
Not every tool should be callable without review. MCP hosts implement authorisation policies that control which tools the model may invoke. In Claude Desktop, for example, a confirmation dialog appears when the model wishes to use a tool for the first time. The user may approve individual calls, approve all calls to a specific tool, or deny the request.
For production deployments, server-side authorisation should also be implemented. A client request for a tool call does not in itself oblige the server to execute it. Inputs should be validated, permissions checked, and access controls enforced.
Data Access Control
Resources expose data to the AI model, which means that sensitive data could potentially reach the model’s context window. MCP servers should be designed in accordance with the principle of least privilege:
- Expose only the data the AI genuinely requires.
- Implement row-level and column-level filtering.
- Redact sensitive fields (passwords, API keys, personally identifiable information) before they are returned.
- Use read-only database connections for query tools.
Credential Management
MCP servers frequently require credentials in order to access external APIs, including GitHub tokens, database passwords and API keys. Recommended practices include the following.
- Pass credentials via environment variables rather than command-line arguments, which may appear in process listings.
- Use secrets managers such as AWS Secrets Manager or HashiCorp Vault for production deployments.
- Rotate credentials regularly.
- Never log credentials.
.claude/settings.json committed to a repository, credentials should never be included directly. Environment variable references or a separate, gitignored secrets file should be used instead.
Sandboxing and Audit Logging
For tools that execute code or run shell commands, sandboxing is important. The following measures should be considered.
- Run MCP servers in containers with limited permissions.
- Use filesystem access controls to restrict which directories are accessible.
- Implement timeout mechanisms for long-running operations.
- Log every tool call with its inputs and outputs for audit purposes.
- Implement rate limiting to prevent abuse.
User Consent Model
The MCP specification encourages a user consent model in which potentially hazardous operations require explicit approval. Before a tool deletes a file, sends an email or deploys code, the user should be asked to confirm. Most MCP hosts implement this at the UI level, but server-side safeguards form an important additional layer.
Building Production MCP Servers
Moving from a prototype MCP server to a production-ready one involves several engineering concerns.
Error Handling
MCP tools should never raise unhandled exceptions. Errors should be caught, descriptive error messages returned, and the isError flag in tool results used to signal failures.
@mcp.tool()
async def query_database(sql: str) -> str:
"""Execute a SQL query."""
try:
# Validate input
if not sql.strip().upper().startswith("SELECT"):
return "Error: Only SELECT queries are allowed for safety."
# Execute with timeout
result = await asyncio.wait_for(
execute_query(sql),
timeout=30.0
)
return json.dumps(result, default=str)
except asyncio.TimeoutError:
return "Error: Query timed out after 30 seconds. Try a simpler query."
except sqlite3.OperationalError as e:
return f"SQL Error: {e}. Check your query syntax."
except Exception as e:
logger.exception("Unexpected error in query_database")
return f"Internal error: {type(e).__name__}. The issue has been logged."
Logging and Monitoring
For MCP servers, logs should be written to stderr rather than stdout, which is reserved for the JSON-RPC protocol when stdio transport is used. Structured logging should include request IDs, tool names, execution times and error details. For HTTP-based servers, integration with standard monitoring tools such as Prometheus, Grafana or Datadog is recommended.
Testing
MCP servers should be tested at multiple levels.
- Unit tests: individual tool functions are tested with known inputs and expected outputs.
- Integration tests: the MCP SDK’s test client is used to simulate the complete protocol flow (initialize, list tools, call tool, verify result).
- End-to-end tests: a real MCP host such as Claude Code is connected to the server and the complete workflow is verified.
# Example: Testing with the MCP SDK's test utilities
import pytest
from mcp.client.session import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters
@pytest.mark.asyncio
async def test_weather_tool():
server_params = StdioServerParameters(
command="python",
args=["weather_server.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# List available tools
tools = await session.list_tools()
tool_names = [t.name for t in tools.tools]
assert "get_weather" in tool_names
# Call the weather tool
result = await session.call_tool(
"get_weather",
arguments={"city": "London"}
)
assert "London" in result.content[0].text
assert "Temperature" in result.content[0].text
Deployment Options
MCP servers can be deployed in several ways, depending on requirements.
- Local binary or script: the simplest option. The server script is distributed and users run it locally via stdio. This option is well suited to personal tools and open-source distribution.
- Docker container: the server is packaged with all dependencies. Users pull the image and point their MCP client at the container. This approach provides consistency across environments.
- Cloud function: deployment as an AWS Lambda, Google Cloud Function or Azure Function, using the HTTP+SSE transport. Scales automatically with pay-per-invocation pricing.
- Dedicated service: the server runs as a persistent web service on Kubernetes, ECS or a virtual machine. This deployment model is best suited to high-traffic, low-latency or shared team scenarios.
The Future of MCP
MCP remains in its early phase, but the trajectory is clear. The following directions are particularly noteworthy.
Growing Industry Adoption
MCP is no longer solely Anthropic’s project. Microsoft has added MCP support to VS Code and GitHub Copilot. Google has indicated interest. The open-source community is producing hundreds of servers. When major competitors adopt a common standard, the standard has typically prevailed. HTTP, JSON and SQL share the same trajectory: no single company owns them, which is precisely why they dominate.
MCP Marketplaces
Just as app stores transformed mobile platforms and browser extension stores transformed the web, MCP marketplaces are emerging. Smithery.ai is an early example: a registry that allows users to discover, install and rate MCP servers. More polished marketplaces with one-click installation, security audits and verified publishers can be expected.
Server-to-Server Communication
The current MCP model is host-to-server: an AI application connects to MCP servers. A natural extension concerns AI agents that use other agents’ tools. Server-to-server MCP communication would enable composable AI systems in which a planning agent delegates tasks to specialised agents, each with its own MCP tools. This is the architecture that will support complex, multi-step AI workflows.
Authentication Standards
OAuth integration for MCP is under active development. This will permit MCP servers to use standard OAuth flows for authentication, simplifying the construction of servers that access user data from third-party services such as Google, Microsoft and Salesforce with appropriate authorisation. Users will no longer be required to generate personal access tokens manually.
Streaming and Performance
Current MCP tools return complete results. Planned improvements include streaming results, which are useful for large dataset queries or real-time data, progress reporting for long-running operations, and partial results that the model can begin processing before the tool finishes. The newer Streamable HTTP transport is a step in this direction.
The Interface Layer for AI
As AI models that can reason, plan and act autonomously become more capable, they will require a standardised way to interact with the digital world. MCP is positioning itself as that interface layer. Just as operating systems provide a standardised interface between applications and hardware, MCP provides a standardised interface between AI models and tools. The model does not need to know how GitHub’s API operates. It only needs to know how to speak MCP.
Getting Started: Your Next Steps
The reader now understands what MCP is, how it operates architecturally, what the three primitives do, how the transport layer functions, and how to build servers in both Python and TypeScript. The following steps support practical application of that knowledge.
Try a Pre-Built MCP Server
The fastest way to experience MCP is to install Claude Desktop and add a pre-built server. The filesystem server is a useful starting point, since it enables Claude to read and search files on the user’s computer.
// claude_desktop_config.json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/you/Documents"
]
}
}
}
Restart Claude Desktop and then ask: “What files are in my Documents folder?” Claude will use the filesystem MCP server to respond.
Build Your Own Server
One of the examples in this article, either the Python weather server or the TypeScript database server, can be taken as a starting point and adapted to a specific use case. Potential applications include a server that queries an internal API, searches personal notes or manages a task list. Begin simply: one or two tools, stdio transport and local execution.
Integrate with the Development Workflow
Developers who use Claude Code may add MCP servers that enhance the development workflow. The GitHub server allows Claude to create issues and pull requests. A database server allows Claude to query the development database. A deployment server may allow Claude to trigger deployments. Each additional server expands Claude Code’s capabilities without requiring changes to Claude Code itself.
Contribute to the Ecosystem
The MCP ecosystem is still young, which creates substantial opportunities to contribute. A developer may build a server for a tool or service that does not yet have one, improve an existing server with better error handling, additional tools or documentation, or submit a pull request to the specification when a use case is found to be inadequately covered.
Essential Resources
- MCP Specification: spec.modelcontextprotocol.io, the authoritative source for the protocol.
- MCP Documentation: modelcontextprotocol.io, containing guides, tutorials and SDK references.
- Python SDK:
pip install mcp, the official Python SDK including FastMCP. - TypeScript SDK:
npm install @modelcontextprotocol/sdk, the official TypeScript SDK. - Reference Servers: github.com/modelcontextprotocol/servers, hosting official and community servers.
- Claude Code Documentation: docs.anthropic.com/en/docs/claude-code, including MCP configuration guidance.
Final Thoughts
The Model Context Protocol is one of those rare technologies that addresses a problem so fundamental that, once understood, the prior state seems untenable. Before MCP, connecting AI to tools was an artisanal craft: hand-built, fragile and duplicated endlessly across every application and every vendor. After MCP, it is an engineering discipline: standardised, composable and reusable.
The N times M problem is real. Every AI company was constructing the same GitHub integration, the same Slack integration and the same database connector, each slightly different, each maintained separately, each failing in its own way. MCP collapses that complexity into N plus M, and the results are already visible: hundreds of servers, dozens of compatible hosts, and a community that is expanding faster than almost any open-source project in the AI space.
MCP is more than an engineering convenience, however. It represents a conceptual shift in how AI capabilities are organised. Rather than building monolithic AI applications that attempt to perform every function, MCP enables a modular architecture in which capabilities are distributed across specialised servers. Weather data has a server. GitHub access has a server. A query interface for a proprietary database can be constructed in an afternoon.
The analogy with HTTP is not hyperbole. HTTP did not merely simplify the retrieval of web pages; it enabled an entire ecosystem of web servers, web applications, CDNs, APIs and services that no one could have predicted in 1991. MCP carries the same potential. The AI tooling ecosystem is at its beginning, and MCP is the protocol that will underpin it.
Developers should consider building MCP servers. Companies with internal tools should consider exposing them via MCP. Organisations evaluating AI platforms should prioritise those that support MCP. The protocol is open, the SDKs are mature and the ecosystem is ready. What remains is the server.
References
- Anthropic. “Introducing the Model Context Protocol.” Anthropic Blog, November 2024. anthropic.com/news/model-context-protocol
- Model Context Protocol. “MCP Specification.” spec.modelcontextprotocol.io
- Model Context Protocol. “Documentation and Guides.” modelcontextprotocol.io
- GitHub. “Model Context Protocol Servers Repository.” github.com/modelcontextprotocol/servers
- Anthropic. “Claude Code Documentation.” docs.anthropic.com/en/docs/claude-code
- Microsoft. “Language Server Protocol.” microsoft.github.io/language-server-protocol
- JSON-RPC Working Group. “JSON-RPC 2.0 Specification.” jsonrpc.org/specification
Disclaimer: This article is for informational and educational purposes only. References to specific companies, products, or technologies do not constitute endorsements. Technology landscapes evolve rapidly—always verify details against official documentation.
Leave a Reply