What Is MCP and Why Does It Matter
The Model Context Protocol (MCP) solves a fundamental integration problem in AI development. Every AI application needs to access external data, whether it is a database, an API, a file system, or a web service. Before MCP, each integration was custom-built: your LangChain agent needed a custom tool wrapper for PostgreSQL, another for Slack, another for GitHub, and so on. If you switched from LangChain to CrewAI, you rewrote all those integrations.
MCP standardizes this by defining a client-server protocol for tool and resource access. An MCP server exposes capabilities like tools, resources, and prompts through a well-defined JSON-RPC interface. An MCP client, built into your AI application, connects to one or more MCP servers and makes their capabilities available to the LLM. The protocol handles discovery, authentication, parameter validation, and response formatting.
Think of MCP as USB for AI applications. Just as USB lets any peripheral work with any computer through a standard interface, MCP lets any AI application access any data source through a standard protocol. A single MCP server for PostgreSQL can be used by Claude Desktop, a custom LangChain agent, a CrewAI workflow, or any other MCP-compatible client.
MCP was created by Anthropic and released as an open standard. The ecosystem is growing rapidly, with MCP servers available for databases, file systems, web APIs, code repositories, communication tools, and more. Understanding MCP is becoming essential for AI developers because it dramatically reduces the integration burden of building production AI systems.
MCP Architecture: Clients, Servers, and Transports
The MCP architecture consists of three layers: hosts, clients, and servers. The host is the AI application that the user interacts with, such as Claude Desktop or your custom agent application. The client is a component within the host that manages connections to MCP servers. Each client maintains a one-to-one connection with a single server, but a host can have multiple clients connecting to different servers simultaneously.
MCP servers expose three types of capabilities. Tools are functions the LLM can call, like 'query_database' or 'send_email'. Resources are data sources the application can read, like files or database tables. Prompts are reusable prompt templates that the server provides for common tasks.
Communication between clients and servers uses JSON-RPC 2.0 over one of two transport mechanisms. The stdio transport runs the server as a subprocess and communicates via standard input and output. This is the simplest option for local servers. The SSE (Server-Sent Events) transport communicates over HTTP, enabling remote servers and networked deployments. The upcoming Streamable HTTP transport will replace SSE as the preferred remote transport.
The connection lifecycle follows a defined sequence: the client sends an initialize request with its protocol version and capabilities, the server responds with its own capabilities and the tools and resources it provides, and then the client can start making tool calls and resource reads. This negotiation ensures compatibility and lets both sides adapt to each other's capabilities. The protocol also supports notifications for real-time updates when resources change on the server side.
Building an MCP Server in Python
Building an MCP server is straightforward with the official Python SDK. You create a server instance, register tools and resources using decorators, and specify a transport. Let us build a simple MCP server that provides weather information and a note-taking capability.
Start by installing the MCP Python SDK. Then create a Server instance with a name and version. Register tools using the @server.tool() decorator on async functions. Each tool function receives its arguments as parameters and returns a list of content objects. The decorator automatically generates the tool schema from the function's docstring and type hints.
For resources, use the @server.resource() decorator. Resources are identified by URIs and return content that the LLM can read. A resource might represent a file (file:///path/to/document), a database record (db://users/123), or any other data source.
The server can also expose prompt templates using the @server.prompt() decorator. These are reusable prompt structures that the client can discover and use, ensuring consistent interactions for common tasks.
To run the server, use the stdio transport for local development or the SSE transport for networked access. The SDK handles all the JSON-RPC serialization, capability negotiation, and error handling. Your tool functions just need to implement the business logic and return results.
One important pattern is input validation. While the MCP SDK validates parameter types against your function signatures, add explicit validation for business rules: check that database queries are read-only, that file paths are within allowed directories, and that API calls respect rate limits. Security is a shared responsibility between the protocol and your implementation.
Connecting MCP Servers to AI Applications
Once you have an MCP server running, the next step is connecting it to your AI application. The simplest option is Claude Desktop, which has built-in MCP support. Add your server to the Claude Desktop configuration file, and the tools become available in your conversations immediately. This is ideal for personal productivity tools.
For custom applications, use the MCP Python client SDK. Create a ClientSession that connects to your server via the appropriate transport. After initialization, call client.list_tools() to discover available tools and client.call_tool() to invoke them. Integrate this with your LangChain or LangGraph agent by wrapping MCP tools as LangChain tools.
The integration pattern with LangChain is clean: for each MCP tool, create a LangChain @tool function that delegates to client.call_tool(). The LangChain tool's docstring and parameter schema are derived from the MCP tool's schema, so the LLM sees the same information regardless of the integration layer. This lets you mix MCP tools with native LangChain tools in the same agent.
For production deployments, consider running MCP servers as microservices behind a gateway. Each server handles one domain, such as database access or email, and your AI application connects to multiple servers simultaneously. This microservice architecture lets you scale, update, and secure each capability independently. Use the SSE transport for networked communication and add authentication headers to protect your MCP endpoints.
Monitoring MCP connections is important in production. Log all tool calls with their parameters and results, track latency, and set up alerts for failed connections. The MCP protocol includes error codes that help diagnose issues: connection failures, timeout errors, and validation errors each have specific codes that your monitoring system can track.
MCP Best Practices and the Growing Ecosystem
As MCP adoption grows, several best practices have emerged from production deployments. First, design your tools with the LLM in mind. Tool descriptions should clearly explain what the tool does, when to use it, and what each parameter means. Ambiguous descriptions lead to incorrect tool calls. Test your tool descriptions by asking an LLM to choose between your tools for various tasks and verify it picks the right one.
Second, implement proper error handling in your servers. Return descriptive error messages that help the LLM recover. Instead of a generic 'query failed' error, return 'Query failed: table users does not exist. Available tables: customers, orders, products.' This gives the LLM enough context to self-correct and try again with the right table name.
Third, use resources for read-heavy data access and tools for actions. If the LLM frequently needs to read a configuration file, expose it as a resource rather than a tool. Resources are designed for efficient data retrieval and support caching, while tools are designed for actions with side effects.
The MCP ecosystem is expanding rapidly. Community-maintained servers exist for PostgreSQL, MySQL, MongoDB, Elasticsearch, GitHub, GitLab, Slack, Discord, Google Workspace, AWS services, and many more. The MCP specification itself is evolving with proposals for streaming tool responses, multimodal content, and enhanced security features.
Looking forward, MCP is becoming the standard integration layer for AI applications. Just as REST APIs standardized web service communication, MCP is standardizing AI-to-tool communication. Investing time in understanding MCP now pays dividends as the ecosystem matures and more enterprise tools provide official MCP servers.
Code Example
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent
server = Server("weather-server")
@server.tool()
async def get_weather(city: str, units: str = "celsius") -> list:
"""Get current weather for a city. Use celsius or fahrenheit."""
# In production, call a real weather API
weather_data = {"city": city, "temp": 28, "condition": "Sunny"}
return [TextContent(
type="text",
text=f"{city}: {weather_data['temp']}°C, {weather_data['condition']}"
)]
@server.tool()
async def save_note(title: str, content: str) -> list:
"""Save a note with a title and content."""
# In production, persist to a database
return [TextContent(type="text", text=f"Note '{title}' saved.")]
async def main():
async with stdio_server() as (read, write):
await server.run(read, write)