Anthropic Model Context Protocol: What Developers Must Implement Now
Your AI integration works fine in demos, but falls apart the moment the model needs to read a file, call an API, or check a database. The root problem isn't the model β it's that there's no standard way to give it reliable, structured access to your systems. That's exactly the gap Anthropic's Model Context Protocol (MCP) was designed to close.
What Is Model Context Protocol (and Why Should You Care)?
MCP is an open protocol that standardizes how AI models connect to external tools and data sources. Think of it as a USB-C port for AI: instead of building a custom integration every time you want Claude (or any MCP-compatible model) to interact with a service, you define the interface once using the protocol and everything that speaks MCP can talk to it.
Anthropic released MCP as an open specification in late 2024, and adoption has moved quickly. Major developer tool companies, cloud providers, and AI frameworks have started shipping MCP-compatible servers. If you're building any AI-powered product that interacts with real data or services, this is infrastructure you need to understand now β not in six months.
What You'll Learn
- How the MCP client-server architecture is structured and where each piece lives
- The three primitives (Tools, Resources, Prompts) and when to use each one
- How to stand up a minimal MCP server in Python
- How to wire a client to consume your server's capabilities
- Security boundaries you must enforce before going to production
How MCP Works: The Architecture at a Glance
MCP follows a client-server model. The host is the application the user interacts with β a chat UI, an IDE plugin, or your own app. The client lives inside the host and manages the connection to one or more MCP servers. Each server exposes a set of capabilities: tools the model can call, resources it can read, and prompt templates it can use.
The model itself sits between the client and the server. When it needs to do something β look up a record, run a query, call an endpoint β it requests a tool call. The client routes that call to the right server, gets the result, and returns it to the model as context. The model then continues its response with real data in hand.
All communication uses JSON-RPC 2.0 over a transport layer. For local processes, stdio is the default. For remote servers, HTTP with Server-Sent Events (SSE) is the standard transport. This means you can run MCP servers locally during development and deploy them over HTTPS in production without changing the protocol layer.
MCP doesn't replace your existing APIs β it wraps them in a standard interface that AI models can discover and use reliably.
The Three Core Primitives: Tools, Resources, and Prompts
Understanding when to use each primitive is the first real design decision you'll face when building an MCP server.
Tools
Tools are functions the model can invoke. They take structured input, perform an action (write to a database, call an external API, run a computation), and return a result. If you've used OpenAI's function calling, this concept will feel familiar β but MCP standardizes it across any model and client that implements the spec. You can read more about how AI tool-calling compares across providers in the breakdown of where AI agents break down in production.
Resources
Resources are read-only data sources the model can access. A resource has a URI and returns content β a file, a database row, an API response snapshot. Unlike tools, resources are not expected to have side effects. Use them when the model needs to read context without mutating anything. Resources can also be subscribed to, meaning the server can push updates when the underlying data changes.
Prompts
Prompts are reusable, parameterized message templates stored on the server. They let you define structured workflows β "summarize this ticket", "generate a migration for this schema" β that the client can invoke by name. Prompts are useful when you want to give users a menu of common tasks without embedding that logic in the host application.
Setting Up Your First MCP Server
The fastest way to build an MCP server is with the official Python SDK, mcp. Install it with pip:
pip install mcp
Here's a minimal server that exposes one tool β a function that looks up a user record by ID from a mock data store:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
app = Server("user-lookup")
USERS = {
"u001": {"name": "Alice", "role": "admin"},
"u002": {"name": "Bob", "role": "viewer"},
}
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="get_user",
description="Retrieve a user record by their ID.",
inputSchema={
"type": "object",
"properties": {
"user_id": {
"type": "string",
"description": "The unique user identifier."
}
},
"required": ["user_id"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == "get_user":
user_id = arguments.get("user_id")
user = USERS.get(user_id)
if user:
return [types.TextContent(type="text", text=str(user))]
return [types.TextContent(type="text", text=f"No user found for ID: {user_id}")]
raise ValueError(f"Unknown tool: {name}")
if __name__ == "__main__":
import asyncio
asyncio.run(stdio_server(app))
Run this script and it will listen on stdin/stdout, ready for an MCP client to connect. The list_tools handler is what the client calls to discover available capabilities. The call_tool handler does the actual work when the model requests a tool invocation.
For a remote server, swap stdio_server for the SSE transport and put it behind a standard HTTPS endpoint. The rest of your handler logic stays identical.
Connecting a Client: Wiring MCP Into Your App
On the client side, the SDK gives you an ClientSession that manages the connection lifecycle. Here's how to connect to the stdio server above and list its tools:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
server_params = StdioServerParameters(
command="python",
args=["server.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print("Available tools:", [t.name for t in tools.tools])
result = await session.call_tool("get_user", {"user_id": "u001"})
print("Result:", result.content[0].text)
asyncio.run(main())
In a real application, you'd pass the tool list to Claude via the tools parameter in the Messages API. When Claude returns a tool-use block, you route that call through the ClientSession, get the result, and feed it back to the model as a tool_result message. This is the full loop that makes an MCP-powered agent tick.
If you're already familiar with how Claude handles structured outputs and want to understand how model capability differences affect this loop, the Claude 4 Opus developer testing guide covers practical behavioral differences worth knowing before you build.
Authentication, Security, and Trust Boundaries
MCP's flexibility is also its biggest risk surface. A model that can call tools can potentially be tricked into calling the wrong ones with crafted inputs. Before you ship anything to production, treat these points as non-negotiable.
Validate all tool inputs server-side
The JSON schema in your list_tools response is a hint to the model, not a hard guard. Your call_tool handler must validate every argument independently. Never trust that the model passed what you specified β validate types, ranges, and allowed values before touching any downstream system.
Scope tool permissions tightly
Each MCP server should do one job. Don't build a single server that can read files, send emails, and modify database records. Splitting servers by domain limits blast radius if a prompt injection attack succeeds. Give each server only the credentials it needs to do its specific job.
Use HTTPS and authenticate remote connections
For any server that isn't running as a local subprocess, require HTTPS and implement token-based authentication at the transport layer. The MCP spec doesn't prescribe an auth method, so you can use API keys, OAuth tokens, or mutual TLS depending on your threat model. Don't expose an unauthenticated MCP server on any network.
Log tool calls and their arguments
Every tool invocation should be logged with enough detail to reconstruct what happened. This is critical for debugging unexpected model behavior and for auditing in regulated environments. Structured logs that include the tool name, input arguments, and output are a minimum.
Common Pitfalls When Implementing MCP
Overly broad tool descriptions. The model decides which tool to call based on your description. If your descriptions are vague or overlapping, the model will make poor choices. Write descriptions that are specific about what the tool does, what it returns, and when to use it versus a similar tool.
Returning too much data from resources. Dumping an entire database table into a resource response will eat your context window and increase costs. Resources should return focused, relevant data. Add filtering parameters to your tools so the model can ask for exactly what it needs. The issue of context window economics is worth understanding deeply β the long-context code review comparison illustrates how different models handle large contexts differently.
Blocking the event loop in async handlers. If your tool handler calls a synchronous database driver or does blocking I/O, you'll lock up the server under concurrent requests. Wrap blocking calls with asyncio.to_thread() or use an async-native client library.
Forgetting the initialization handshake. Every MCP session starts with an initialize call where the client and server exchange capabilities. If your client skips this or your server doesn't handle it correctly, subsequent calls will silently fail or return errors that are hard to trace. Always call session.initialize() before anything else.
Shipping stdio-only servers to production. The stdio transport is great for local development and desktop tools, but it doesn't scale. Plan your remote transport (SSE over HTTPS) early so you're not rewriting server bootstrapping code under deadline pressure. The OpenAI Codex CLI guide gives useful context on how local AI tool integrations differ from cloud-deployed ones β the operational gap is larger than it looks.
Wrapping Up: Next Steps to Ship MCP Today
MCP is past the experimental stage. The tooling is stable, the Python and TypeScript SDKs are well-documented, and production deployments are already running at scale. The window to get ahead of this curve is closing.
Here's what to do this week:
- Audit your existing AI integrations and identify one data source or API that your model currently can't reach. That's your first MCP server candidate.
- Build the minimal server using the pattern above β one tool, one resource, working end-to-end before you add complexity.
- Wire it into Claude via the Messages API with the
toolsparameter, run a real conversation through it, and watch how the model uses your tool descriptions to make decisions. - Harden before you deploy β add input validation, scope credentials, and enable structured logging on every tool call.
- Read the MCP specification on the official Anthropic docs to understand the edge cases in resource subscriptions, sampling, and multi-server routing that this article doesn't cover.
The developers who understand this layer now will build better agents faster, with fewer production surprises. Start with one server, ship it, and iterate from there.
Frequently Asked Questions
Does MCP only work with Anthropic's Claude models?
MCP is an open protocol and is not limited to Claude. Any model or client that implements the MCP specification can use MCP servers. Adoption is growing across multiple AI frameworks and providers, though Anthropic created the spec.
What's the difference between MCP tools and OpenAI function calling?
Both let a model request structured actions during a conversation, but MCP standardizes the interface across any model and any client rather than tying it to a single provider's API. MCP also introduces Resources and Prompts as additional primitives that function calling doesn't cover.
Can I run an MCP server in production without exposing it publicly?
Yes. You can run MCP servers as private services inside your infrastructure, accessible only to your application's client over an internal network or VPN. The SSE transport works over any HTTPS endpoint, including private ones behind an API gateway.
How do I handle errors when a tool call fails inside an MCP server?
Return a structured error response in your call_tool handler rather than raising an unhandled exception. The MCP spec defines an error content type you can use to pass the error message back to the model, which can then decide how to handle it or inform the user.
Is MCP suitable for real-time applications where the model needs live data?
Yes, particularly through MCP's resource subscription feature, which allows servers to push updates to the client when underlying data changes. For high-frequency real-time use cases you'll want to pair this with a fast transport and carefully consider context window costs for each update.
π€ Share this article
Sign in to saveRelated Articles
Comments (0)
No comments yet. Be the first!