TL;DR
-
MCP is an open protocol that standardizes how LLM applications connect to external tools and data sources - one interface instead of custom integrations per tool.
-
MCP has three primitives: Tools (actions), Resources (read-only data), and Prompts (reusable templates) - servers expose these; clients (LLM apps) consume them.
-
It uses a client-server architecture - MCP servers wrap external systems; MCP clients (like Cursor or Claude Desktop) connect to servers and expose capabilities to the model.
-
MCP solves the N×M integration problem - without it, every LLM app needs custom code for every tool. With MCP, build one server, connect any client.
-
MCP is production-ready but evolving - Anthropic open-sourced it in late 2024; adoption is growing across IDEs, agents, and enterprise platforms.
Why This Matters
Before MCP, connecting an LLM to external tools meant writing custom integration code for every combination of application and tool. Cursor needed custom code for GitHub, Slack, and databases. Claude Desktop needed separate integrations for the same tools. Every agent framework reinvented the tool connection layer.
MCP replaces this fragmentation with a standard protocol. Build an MCP server for your database once - any MCP-compatible client (Cursor, Claude Desktop, custom agents) can connect to it without custom code. This is the USB-C of AI tool integration: one port, many devices.
For AI engineers, MCP changes how you think about tool architecture. Instead of embedding tool logic in your agent code, you expose capabilities as MCP servers that any LLM application can discover and use. This separation of concerns makes tools reusable, testable, and independently deployable.
The Problem MCP Solves
The tool integration landscape before MCP:
Before MCP, every LLM application needed bespoke integrations for each external tool. MCP standardizes those connections through a host–client–server model defined by Anthropic's open protocol.
An MCP host application manages multiple MCP clients, each maintaining a dedicated one-to-one connection to an MCP server that wraps an external system like GitHub, a database, or the filesystem.
Source: Model Context Protocol Docs Repository
N applications × M tools = N×M custom integrations. Each integration is built, maintained, and debugged independently. Tool definitions are inconsistent. Authentication is duplicated. Updates to a tool require changes in every client.
MCP solves this:
Each MCP client maintains a dedicated connection to one MCP server, letting the host aggregate tools, resources, and prompts from multiple backends through a single protocol.
MCP sessions begin with JSON-RPC initialization, capability exchange, and then bidirectional tool calls where the host forwards LLM-initiated requests to servers and returns results.

Source: Model Context Protocol Docs Repository
N clients + M servers, connected through one protocol. Build the server once; any client connects.
What Is MCP?
The Model Context Protocol (MCP) is an open standard introduced by Anthropic in November 2024 for connecting LLM applications to external data sources and tools. It defines a client-server protocol based on JSON-RPC 2.0 over stdio or HTTP/SSE transports.
MCP provides three capability primitives:
| Primitive | Purpose | Example |
|---|---|---|
| Tools | Actions the LLM can invoke | create_issue, run_query, send_message |
| Resources | Read-only data the LLM can access | File contents, database schemas, API docs |
| Prompts | Reusable prompt templates | Code review template, bug report formatter |
An MCP server exposes tools, resources, and prompts for a specific system (GitHub, PostgreSQL, Slack). An MCP client connects to servers and makes their capabilities available to the LLM.
MCP Server (GitHub) MCP Client (Cursor) LLM
├── tools/ ├── connects to servers ├── sees available tools
│ ├── create_issue ├── lists tools/resources ├── selects tool
│ ├── search_repos └── forwards tool calls └── generates response
│ └── list_prs
├── resources/
│ ├── repo://README
│ └── repo://file/path
└── prompts/
└── code_review
How MCP Works
Protocol Architecture
MCP separates the data layer (tools, resources, prompts over JSON-RPC) from the transport layer (stdio for local servers, HTTP/SSE for remote services).

Source: Model Context Protocol Docs Repository
Transport Layers
MCP supports two transport mechanisms:
| Transport | Use case | How it works |
|---|---|---|
| stdio | Local servers | Client spawns server as subprocess; JSON-RPC over stdin/stdout |
| HTTP/SSE | Remote servers | Server-Sent Events for server→client; HTTP POST for client→server |
Local development typically uses stdio - the client launches the server process:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
}
}
}
Remote/production deployments use HTTP/SSE for server-side MCP servers accessible over the network.
MCP vs Function Calling
| Aspect | Function Calling | MCP |
|---|---|---|
| Scope | Single application | Cross-application standard |
| Tool definition | In application code | Server-side, discoverable |
| Transport | Provider API (OpenAI, Anthropic) | JSON-RPC over stdio or HTTP |
| Discovery | Static - defined at build time | Dynamic - servers advertise capabilities |
| Reusability | Per-application | One server, many clients |
| State | Client manages | Server can maintain state |
MCP does not replace function calling - it standardizes what function calling connects to. The MCP client translates MCP tools into function calling format for the LLM.
Architecture
In production, each external system is exposed as one MCP server; any compatible client discovers capabilities and invokes tools without bespoke integration code per application.

Source: Model Context Protocol Docs Repository
Step-by-Step Flow
Building and connecting an MCP server:
-
Identify the capability - What system do you want to expose? (database, API, file system, internal service)
-
Create an MCP server - Use the MCP SDK to define tools, resources, and prompts.
-
Implement tool handlers - Write the code that executes when the LLM calls each tool.
-
Configure the client - Add the server to the MCP client's configuration (e.g., Cursor settings, Claude Desktop config).
-
Client discovers capabilities - On connection, the client calls
tools/list,resources/list,prompts/list. -
LLM uses tools - When the user asks a question, the LLM selects MCP tools; the client forwards calls to the server.
-
Server executes and returns - The server runs the tool, returns results through the protocol.
-
Monitor and iterate - Log tool usage, refine descriptions, add new tools as needed.
Real Production Example
Building a custom MCP server that exposes an internal customer API:
# server.py - MCP server for customer lookup
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import httpx
import json
import os
server = Server("customer-api")
API_BASE = os.environ.get("CUSTOMER_API_URL", "http://localhost:8080/api")
API_KEY = os.environ.get("CUSTOMER_API_KEY", "")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="lookup_customer",
description="Look up customer details by email or customer ID.
Returns name, plan, status, and billing info.",
inputSchema={
"type": "object",
"properties": {
"email": {"type": "string", "description": "Customer email address"},
"customer_id": {"type": "string", "description": "Customer ID (e.g., CUST-12345)"},
},
},
),
Tool(
name="list_recent_orders",
description="List recent orders for a customer.
Returns order ID, date, total, and status.",
inputSchema={
"type": "object",
"properties": {
"customer_id": {"type": "string", "description": "Customer ID"},
"limit": {"type": "integer", "description": "Max orders to return", "default": 10},
},
"required": ["customer_id"],
},
),
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
async with httpx.AsyncClient(base_url=API_BASE, headers=headers, timeout=10.0) as client:
if name == "lookup_customer":
params = {k: v for k, v in arguments.items() if v}
response = await client.get("/customers", params=params)
response.raise_for_status()
return [TextContent(type="text", text=json.dumps(response.json(), indent=2))]
elif name == "list_recent_orders":
customer_id = arguments["customer_id"]
limit = arguments.get("limit", 10)
response = await client.get(
f"/customers/{customer_id}/orders",
params={"limit": limit},
)
response.raise_for_status()
return [TextContent(type="text", text=json.dumps(response.json(), indent=2))]
return [TextContent(type="text", text=json.dumps({"error": f"Unknown tool: {name}"}))]
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream, server.create_initialization_options())
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Client configuration (Cursor/Claude Desktop):
{
"mcpServers": {
"customer-api": {
"command": "python",
"args": ["/path/to/server.py"],
"env": {
"CUSTOMER_API_URL": "https://api.internal.acme.com/v1",
"CUSTOMER_API_KEY": "${CUSTOMER_API_KEY}"
}
}
}
}
Once configured, any MCP client can ask "Look up customer john@example.com" and the LLM will invoke lookup_customer through the MCP protocol.
Design Decisions
| Decision | Option A | Option B | When to choose |
|---|---|---|---|
| Server scope | One server per system | Monolithic server | One per system for modularity and independent deployment; monolithic for simple setups |
| Transport | stdio (local) | HTTP/SSE (remote) | stdio for local dev tools; HTTP/SSE for shared/remote servers in production |
| Tool granularity | Fine-grained tools | High-level tools | Fine-grained for flexibility; high-level for reliability (fewer tool selection errors) |
| Auth model | Server manages credentials | Client passes tokens | Server-managed for simplicity; client-passed for multi-tenant scenarios |
| Resource exposure | Expose schemas and docs | Expose live data | Schemas for context; live data when the LLM needs current state |
⚠ Common Mistakes
-
Exposing too many tools - An MCP server with 30 tools overwhelms the model's selection accuracy. Keep servers focused: 5–10 tools per server, one server per domain.
-
Vague tool descriptions - MCP tool descriptions are how the LLM decides when to use them.
"Query the database"is useless."Execute a read-only SQL query against the analytics database. Use for revenue, customer, and order data."is actionable. -
No error handling in tool handlers - Unhandled exceptions crash the MCP server. Wrap all handlers in try/except and return structured error messages.
-
Hardcoding credentials in server config - Use environment variables and secret managers. MCP config files often end up in version control.
-
Ignoring resource capabilities - MCP resources let the LLM read data without tool calls (database schemas, file contents, API docs). Exposing resources reduces unnecessary tool invocations.
-
Not versioning servers - MCP servers are APIs. Version your tool schemas, maintain backward compatibility, and communicate breaking changes to client configurations.
Where It Breaks Down
-
Protocol maturity - MCP is young (late 2024). Breaking changes, incomplete SDK support, and missing features (streaming tool results, batch operations) are expected during rapid adoption.
-
No built-in auth protocol - MCP does not standardize authentication between clients and servers. You implement auth in your server or transport layer, leading to inconsistent patterns.
-
Latency overhead - JSON-RPC serialization + process communication (stdio) adds 10–50ms per tool call compared to in-process function calling. Acceptable for most use cases; problematic for high-frequency tool loops.
-
Stateless by default - MCP servers are typically stateless. Tools requiring persistent connections (WebSocket feeds, long-running jobs) need custom state management.
-
Discovery limits - All tools are listed at connection time. No runtime tool discovery based on conversation context. Large tool sets degrade model selection accuracy.
Running in Production
Best Practice
✅ Best Practices - Instrument every stage, version embedding models, enforce access control at retrieval time, and evaluate on a fixed golden set before shipping changes.
| Dimension | Consideration |
|---|---|
| Scaling | MCP servers are independent processes. Scale by running multiple server instances behind a load balancer (HTTP/SSE transport). stdio servers are single-process - one instance per client connection. |
| Latency | stdio transport: 10–30ms overhead per call. HTTP/SSE: 20–100ms depending on network. Budget MCP overhead into your agent loop latency calculations. |
| Cost | MCP itself is free (open protocol). Cost comes from the underlying systems tools call (API fees, database queries) and the LLM tokens consumed by tool definitions and results. |
| Monitoring | Log every tool call: server name, tool name, arguments (sanitized), result size, latency, and errors. Track tool usage frequency to identify unused or misused tools. |
| Evaluation | Test MCP tool selection with the same golden scenarios used for function calling. Verify tool descriptions lead to correct selection across different LLM clients. |
| Security | MCP servers execute with the credentials configured in the client. Restrict server permissions to minimum required. Audit tool calls. Never expose admin-level tools through MCP without authentication. Run servers with limited OS permissions. |
Important
MCP servers execute with the credentials and permissions configured in the client. A compromised MCP config or malicious server can access everything the credentials allow. Validate server sources, restrict permissions, and audit tool calls.
Ecosystem
Official MCP Servers (Anthropic-maintained)
@modelcontextprotocol/server-github- GitHub repos, issues, PRs@modelcontextprotocol/server-postgres- PostgreSQL queries and schema@modelcontextprotocol/server-filesystem- Local file read/write@modelcontextprotocol/server-slack- Slack messaging@modelcontextprotocol/server-google-maps- Location and mapping
MCP Clients
- Cursor IDE - Built-in MCP client for development
- Claude Desktop - Native MCP support
- Continue - Open-source AI code assistant
- Custom agents via MCP SDK (Python, TypeScript)
SDKs
@modelcontextprotocol/sdk- TypeScript SDK for building servers and clientsmcp- Python SDK for server and client implementation
Community Servers
- Growing ecosystem of community-built servers for AWS, Notion, Jira, Docker, and more. Check the MCP servers repository for the latest.
Related Technologies
-
Tool Calling - The general pattern MCP standardizes. MCP is to tool calling what HTTP is to network communication.
-
Function Calling - The LLM-facing mechanism that MCP clients translate MCP tools into.
-
AI Agents - Agents consume MCP tools as their primary way to interact with external systems.
-
Agent Architectures - ReAct and Plan-and-Execute patterns that orchestrate MCP tool calls.
-
AI System Architecture - MCP fits into the tool/integration layer of production AI system design.
-
Prompt Engineering - MCP prompts provide reusable templates; tool descriptions are prompt engineering artifacts.
Learning Path
Prerequisites: Tool Calling · Function Calling
Next topics: AI Agents · Agent Architectures · AI System Architecture
Estimated time: 50 min · Difficulty: Intermediate
FAQs
Is MCP only for Anthropic/Claude?
No. MCP is an open protocol. While Anthropic created it, MCP clients and servers work with any LLM. Cursor uses MCP with various models. The protocol is model-agnostic.
Do I need MCP if I already have function calling?
Not necessarily. If your tools are used by one application, function calling is simpler. MCP adds value when you want tools reusable across multiple LLM applications, or when you want standardized tool discovery and configuration.
How is MCP different from LangChain tools?
LangChain tools are Python functions bound to a specific agent framework. MCP tools are protocol-based services usable by any MCP client. LangChain can consume MCP servers via an MCP adapter, combining both approaches.
Can I build an MCP server for my internal API?
Yes - this is the primary use case. Wrap your internal APIs, databases, or services as MCP servers. Any MCP-compatible client (Cursor, Claude Desktop, custom agents) can then access them without custom integration code.
Is MCP secure for production?
MCP provides the transport and protocol, not security. You must implement authentication, authorization, input validation, and audit logging in your servers. Treat MCP servers like microservices - apply the same security practices.
What is the difference between MCP tools and resources?
Tools are actions the LLM invokes (create, update, search). Resources are read-only data the LLM can access (file contents, schemas, documentation). Resources provide context; tools perform operations.
How do I debug MCP servers?
Run the server standalone and test with the MCP Inspector (browser-based debugging tool). Log all tool calls and responses. Test tool descriptions by asking the LLM questions that should trigger each tool.
Can MCP servers call other MCP servers?
Not directly through the protocol. An MCP server can internally call another service (API, database), but MCP client-to-server is the standard connection pattern. Compose at the client level by connecting to multiple servers.
What transports does MCP support?
stdio (local subprocess communication) and HTTP with Server-Sent Events (remote/network). stdio is standard for local development; HTTP/SSE for production deployments and shared servers.
How do I migrate from function calling to MCP?
Extract tool definitions and implementations into an MCP server. Replace in-process tool execution with MCP client calls. The LLM interaction pattern (tool selection, argument generation) remains the same - only the execution layer changes.
References
Further Reading
Summary
-
MCP standardizes how LLM applications connect to external tools and data - one protocol instead of N×M custom integrations. - Three primitives: Tools (actions), Resources (read-only data), Prompts (templates). - Build MCP servers for your systems; any MCP client connects without custom code. - MCP complements function calling - it standardizes the server side, not the LLM interface.
-
Keep servers focused (5–10 tools), write clear descriptions, handle errors, and secure credentials. - MCP is young but adoption is accelerating - invest in MCP servers for tools you want reusable across applications.