Model Context Protocol (MCP) Explained: What It Is and How to Build Servers and Clients

What Is the Model Context Protocol?

The Model Context Protocol (MCP) is an open standard introduced by Anthropic in late 2024 that defines how AI applications connect to external tools, data sources, and services. Before MCP, every team building an LLM application had to write custom integration code for each tool they wanted to expose to a model — a bespoke connector for their database, another for their file system, another for their web search API. MCP replaces that proliferation of one-off integrations with a single, consistent protocol that any AI client can speak to any MCP server.

The analogy Anthropic uses is the USB standard: before USB, every peripheral needed a different port and driver. USB created a universal interface so any peripheral works with any computer. MCP does the same for AI tools — any MCP-compatible agent or application can connect to any MCP server without custom integration work.

Since its release, MCP has been adopted rapidly. Claude Desktop, Cursor, Zed, Continue, Windsurf, and dozens of other AI-powered tools now act as MCP clients. Hundreds of MCP servers have been published for tools ranging from GitHub and Postgres to Slack, Figma, and local file systems.

The Three Primitives: Tools, Resources, and Prompts

Tools are executable functions the model can call — the equivalent of function calling in the OpenAI or Anthropic APIs. A GitHub MCP server might expose tools like create_issue, list_pull_requests, and merge_branch. The model decides when to call them based on their descriptions, exactly as with standard tool calling.

Resources are data that can be read into context — files, database rows, API responses, or any structured content. Resources are identified by URIs and fetched on demand. A Postgres MCP server might expose table schemas as resources, letting the model read the schema before writing a query. Resources are distinct from tools because they are read-only — they add information to context rather than triggering an action.

Prompts are reusable prompt templates the server exposes, optionally parameterised. A code review MCP server might expose a review_pull_request prompt that accepts a diff and a style guide as parameters. Prompts let server authors encode best practices for how to interact with their tools.

The Architecture: Hosts, Clients, and Servers

MCP follows a client-server architecture with three roles. Hosts are the AI applications the user interacts with — Claude Desktop, Cursor, a custom chatbot. The host manages the conversation and decides which MCP servers to connect to. Clients are the MCP protocol layer embedded in each host — a single host can maintain connections to multiple MCP servers simultaneously. Servers are lightweight processes that expose tools, resources, and prompts via the MCP protocol.

The transport layer supports two modes: stdio for local servers launched as subprocesses, and HTTP with Server-Sent Events for remote servers. Most developer tools today use stdio for local integrations and HTTP+SSE for cloud-hosted services.

Building a Python MCP Server

The official Python SDK makes building an MCP server straightforward. Install it first:

pip install mcp

Here is a minimal MCP server exposing a weather tool:

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import json, asyncio

app = Server("weather-server")

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [types.Tool(
        name="get_weather",
        description="Get current weather for a city.",
        inputSchema={
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"]
        }
    )]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    if name == "get_weather":
        data = {"city": arguments["city"], "temp": 18, "condition": "cloudy"}
        return [types.TextContent(type="text", text=json.dumps(data))]
    raise ValueError(f"Unknown tool: {name}")

if __name__ == "__main__":
    asyncio.run(stdio_server(app))

Connecting to Claude Desktop

Claude Desktop reads MCP server configuration from a JSON file. On macOS this lives at ~/Library/Application Support/Claude/claude_desktop_config.json; on Windows at %APPDATA%/Claude/claude_desktop_config.json. Add your server to the mcpServers object:

{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["/path/to/weather_server.py"],
      "env": { "WEATHER_API_KEY": "your-key" }
    }
  }
}

Restart Claude Desktop and your tools appear automatically in any conversation.

Building a TypeScript MCP Server

The TypeScript SDK is the most widely used option for Node.js-based tools:

npm install @modelcontextprotocol/sdk
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  { name: "weather-server", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: "get_weather",
    description: "Get current weather for a city.",
    inputSchema: {
      type: "object",
      properties: { city: { type: "string" } },
      required: ["city"]
    }
  }]
}));

server.setRequestHandler(CallToolRequestSchema, async (req) => {
  if (req.params.name === "get_weather") {
    const city = req.params.arguments?.city as string;
    return { content: [{ type: "text", text: JSON.stringify({ city, temp: 18 }) }] };
  }
  throw new Error("Unknown tool");
});

await server.connect(new StdioServerTransport());

Using MCP Programmatically in Your Agent

You do not need Claude Desktop to benefit from MCP — you can connect to MCP servers from any agent code using the Python client:

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import asyncio

async def run():
    params = StdioServerParameters(command="python", args=["weather_server.py"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print([t.name for t in tools.tools])
            result = await session.call_tool("get_weather", {"city": "Tokyo"})
            print(result.content[0].text)

asyncio.run(run())

This pattern lets you build agent loops that dynamically discover and use tools from any MCP server. When a server adds a new tool, your agent gains access to it at the next session initialisation with no code changes on your end.

Remote MCP Servers over HTTP

For cloud-hosted tools, MCP supports an HTTP transport using Server-Sent Events. Connect using the SSE client:

from mcp.client.sse import sse_client

async with sse_client("https://your-mcp-server.com/sse") as (read, write):
    async with ClientSession(read, write) as session:
        await session.initialize()
        result = await session.call_tool("search_web", {"query": "AI news"})

Remote servers require authentication in production. Pass API keys via HTTP headers configured in the client or via environment variables — never hardcode credentials in source code.

Using Existing MCP Servers

One of the most practical benefits of MCP is the library of ready-made servers you can drop into your workflow. Anthropic maintains official reference servers covering the most common developer needs.

The filesystem server gives the model read and write access to a specified directory. The GitHub server wraps the GitHub REST API with tools for listing repositories, reading files, creating issues, and managing pull requests. The Postgres server connects to a database and lets the model query tables and describe schemas. The Brave Search server provides live web search. The Slack server can read channels and post messages.

To use any of these, add them to your Claude Desktop config. For example, the filesystem server restricted to your projects directory:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/yourname/projects"]
    }
  }
}

Because npx downloads the package on first run, no manual install step is needed.

Security Considerations

MCP servers can take real actions — writing files, calling APIs, sending messages. Scope permissions tightly. Restrict the filesystem server to only the directories the model genuinely needs. Use API keys with minimum required permissions, not admin credentials. Treat an MCP server’s access level as equivalent to what you would give a junior developer: useful for most tasks, but not given the keys to production systems.

Be aware of prompt injection risks. A malicious document or web page could embed instructions designed to hijack tool calls — for example, telling the model to exfiltrate file contents to an external URL. Mitigate this by keeping tool permissions minimal, requiring user confirmation for destructive actions, and auditing what each server can actually do before adding it to your configuration.

For remote MCP servers, always use HTTPS and authenticate every request. Never expose an MCP endpoint to the public internet without authentication, even during development.

Designing Good MCP Servers

The same principles that apply to tool design in direct function calling apply to MCP servers. Keep each server focused on a single domain — a server that does one thing well is more reliable than one wrapping an entire platform. Write thorough tool and resource descriptions, since these are all the model has to go on when deciding what to call. Return clean, structured data and informative error messages rather than raw API responses or silent failures.

Pay attention to startup time. Hosts launch MCP servers as subprocesses during initialisation. A server that takes several seconds to start because it is loading a large model or establishing a database connection will noticeably slow the host application. Keep initialisation fast and defer expensive operations to the first actual tool call.

The MCP Ecosystem in 2026

MCP has moved quickly from a niche Anthropic project to a broadly adopted standard. Major AI development tools — Cursor, Zed, Continue, Windsurf — have all added MCP support, meaning the same server you build for Claude Desktop also works in your IDE. A growing number of SaaS platforms publish official MCP servers for their APIs, reducing integration burden for developers building AI workflows on top of them. The standardisation benefit is real: building one well-designed MCP server now pays off across every client that adopts the protocol. For practitioners building serious AI applications, MCP is worth understanding now — the protocol is stable, the tooling is mature, and the network effects of a shared standard are already compounding.

MCP vs. Direct Tool Calling: When to Use Each

Direct tool calling — defining functions inline in your API call — is simpler and the right choice for most single-application setups. You define your tools once, close to your application code, and call them directly. There is no extra protocol layer, no server process to manage, and the entire integration lives in one place. For the majority of LLM applications, this is sufficient.

MCP adds value in three specific situations. First, when tools need to be shared across multiple applications or agents: rather than reimplementing the same database connector in five different codebases, you build one MCP server and connect everything to it. Second, when you want to use pre-built integrations from the community rather than writing everything yourself — the existing library of MCP servers covers the most common tools already. Third, when you are building a platform where third parties will contribute tools: MCP gives you a standard interface they can target without knowing anything about your internal architecture.

The two approaches are also complementary rather than mutually exclusive. Many MCP servers internally use the same function-calling logic, just wrapped in the MCP transport layer. You can build an application that uses direct tool calling for its core functionality and also connects to MCP servers for supplementary integrations. Start with direct tool calling for speed, and introduce MCP server architecture when the distribution and discovery benefits justify the added complexity.

Sampling: LLMs Inside MCP Servers

One lesser-known MCP capability is sampling — the ability for an MCP server to ask the host application to make an LLM completion on its behalf. This enables servers to use AI as part of their own logic without needing direct API access. A server handling a complex multi-step task might use sampling to break down a request, generate an intermediate plan, or evaluate its own output before returning a result.

Sampling requests flow in the opposite direction from tool calls: the server sends a request to the client, which forwards it to the host, which calls the model and returns the result. The host controls which sampling requests it approves, which is an important security boundary — a server cannot make arbitrary LLM calls without the host’s awareness and consent. For most use cases you will not need sampling, but it is worth knowing it exists for server implementations that need AI-assisted reasoning as part of their own processing pipeline.

Getting Started Quickly

The fastest path to a working MCP integration is to start with an existing server rather than building your own. Clone the official MCP servers repository, pick one that matches a tool you already use, add it to your Claude Desktop config, and spend ten minutes exploring what it can do in conversation. The filesystem server paired with Claude Desktop is a particularly useful starting point — being able to ask Claude to read a project directory, understand its structure, and make targeted edits across multiple files is immediately practical and gives you a clear sense of what agentic AI tooling feels like in practice. Once you understand what well-designed MCP tools feel like from the user side, building your own becomes much more intuitive.

Leave a Comment