intermediate6 sectionsUpdated Jun 15, 2025

Model Context Protocol

The open standard that lets LLM applications seamlessly connect to any external data source or tool.

What is MCP?

The Model Context Protocol (MCP) is an open standard, created by Anthropic, that defines how LLM applications connect to external data sources and tools. Think of it as USB-C for AI — a universal interface that lets any AI application plug into any data source or tool without custom integration code for each combination.

Before MCP, every framework had its own way of defining and connecting tools. If you built a tool integration for LangChain, you had to rebuild it for CrewAI, and again for the OpenAI SDK. This created an M x N problem: M applications times N tools equals M x N custom integrations.

MCP solves this by defining a single protocol that both sides implement. Tool providers build one MCP server. Application developers build one MCP client. Any MCP client can connect to any MCP server. The M x N problem becomes M + N.

MCP has been adopted across the industry — Claude Desktop, VS Code Copilot, Cursor, Windsurf, the OpenAI Agents SDK, and many other tools now support MCP. This makes it a de facto standard for AI tool connectivity.

Client-Server Architecture

MCP follows a client-server architecture with clear separation of roles:

MCP Host — The AI application the user interacts with (Claude Desktop, an IDE, a custom agent). The host manages connections to one or more MCP servers.

MCP Client — A protocol client within the host that maintains a 1:1 connection with a single MCP server. The client handles protocol negotiation, message routing, and capability discovery.

MCP Server — A lightweight program that exposes specific capabilities (tools, data sources, prompts) through the standardized MCP protocol. Servers can be local processes or remote services.

MCP Client-Server Architecture MCP Client-Server Architecture
Fig. MCP client-server architecture — one host, many clients, each with a dedicated 1:1 server connection over JSON-RPC 2.0

This architecture allows a single AI application to connect to multiple specialized servers simultaneously. Claude Desktop, for example, can connect to a database server, a GitHub server, a Slack server, and a file system server — all at once, each through its own MCP client-server connection.

Under the hood, all MCP communication uses JSON-RPC 2.0 as the wire protocol. Every request, response, and notification follows the JSON-RPC 2.0 message format, which provides structured error codes and request ID tracking.

Core Primitives: Resources, Tools, and Prompts

MCP defines three core primitives that servers can expose to clients:

Tools

Functions that the LLM can invoke. These are the most commonly used primitive and map directly to the function calling concept. Tools have a name, description, and JSON Schema for their parameters. Examples: query_database, create_github_issue, send_email.

Resources

Data that the application can read and inject into the LLM's context. Resources are identified by URIs and can be text or binary (images, PDFs). Unlike tools (which are invoked by the model), resources are typically selected by the application or user. Examples: file:///project/readme.md, postgres://db/users/schema.

Prompts

Reusable prompt templates that servers can expose. These are pre-written instructions that encapsulate best practices for interacting with the server's domain. A database MCP server might expose prompts like "Analyze this table" or "Optimize this query" that include the right context and instructions. Prompts can include dynamic arguments. Prompts are explicitly user-controlled (like slash commands), distinguishing them from tools (model-controlled) and resources (application-controlled).

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({ name: "db-server", version: "1.0.0" });

// Register a tool (model-controlled)
server.tool(
  "query_database",
  "Execute a read-only SQL query",
  { sql: z.string().describe("SQL query to execute") },
  async ({ sql }) => ({
    content: [{ type: "text", text: JSON.stringify(await db.query(sql)) }]
  })
);

// Register a resource (application-controlled)
server.resource(
  "db-schema",
  "postgres://mydb/schema",
  async () => ({
    contents: [{ uri: "postgres://mydb/schema", text: schemaText }]
  })
);

// Register a prompt (user-controlled)
server.prompt(
  "analyze_table",
  { table_name: z.string() },
  ({ table_name }) => ({
    messages: [{ role: "user",
      content: { type: "text", text: `Analyze the ${table_name} table` }
    }]
  })
);

Transport Layers: stdio and Streamable HTTP

MCP supports two transport mechanisms for communication between clients and servers:

stdio (Standard Input/Output)

The client launches the MCP server as a subprocess and communicates through standard input and output streams. This is the simplest and most common transport for local servers. It requires no network configuration, is inherently secure (no network exposure), and supports any language that can read stdin and write stdout.

// Claude Desktop config using stdio transport
{
  "mcpServers": {
    "sqlite": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-sqlite",
               "/path/to/database.db"]
    }
  }
}

Streamable HTTP (and legacy HTTP+SSE)

For remote servers, MCP uses Streamable HTTP (which superseded the older HTTP+SSE transport in the 2025-03-26 spec revision). The client sends JSON-RPC 2.0 requests via HTTP POST, and the server can respond directly or upgrade to streaming via Server-Sent Events when needed. This enables remote MCP servers hosted as web services, cloud functions, or containers. It supports authentication, load balancing, and all the operational tooling of standard HTTP services.

Choosing between them is straightforward: use stdio for local tools (file system access, local databases, CLI tools) and HTTP for remote services (cloud APIs, shared team resources, SaaS integrations).

Building an MCP Server

Building an MCP server is surprisingly simple. The official SDKs (TypeScript and Python) handle the protocol details, so you only need to define your tools, resources, and prompts. Here is a minimal example:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

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

// Define a tool
server.tool(
  "get_weather",
  "Get the current weather for a city",
  { city: z.string().describe("City name") },
  async ({ city }) => {
    const response = await fetch(
      `https://api.weather.com/current?city=${city}`
    );
    const data = await response.json();
    return {
      content: [{
        type: "text",
        text: `Weather in ${city}: ${data.temp}°C, ${data.condition}`
      }]
    };
  }
);

// Start the server with stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);

Key practices for building production MCP servers:

  • Write clear descriptions for every tool and parameter. The LLM relies on these descriptions to decide when and how to use your tools.
  • Return structured results with enough context for the LLM to understand and use the data.
  • Implement error handling that returns useful error messages rather than crashing the server.
  • Add authentication for sensitive operations, especially with HTTP transport.
  • Keep servers focused — one server per domain (database, GitHub, Slack) rather than one monolithic server.

The MCP Ecosystem

The MCP ecosystem is growing rapidly. Here is the landscape as it stands:

Official servers — Anthropic maintains reference implementations for common integrations: filesystem, GitHub, GitLab, Google Drive, PostgreSQL, SQLite, Slack, Brave Search, and more. These serve as both useful tools and examples for building your own servers.

Community servers — Hundreds of community-built MCP servers cover everything from Notion and Linear to Docker and Kubernetes. The MCP servers repository catalogs the ecosystem.

Framework adoption — Major agent frameworks have added MCP support: LangChain/LangGraph, OpenAI Agents SDK, Claude Agent SDK, Google ADK, CrewAI, PydanticAI, Mastra, and others. This means tools built as MCP servers are automatically available to agents in any of these frameworks.

Client applications — Claude Desktop, VS Code (via Copilot), Cursor, Windsurf, Zed, and other AI-powered applications support MCP, giving users access to MCP servers directly in their workflow.

The strategic value of MCP is clear: instead of building custom integrations for each framework, build one MCP server and it works everywhere. This is why MCP adoption is accelerating — it is a force multiplier for both tool builders and agent developers. As the ecosystem grows, the network effects compound: more servers attract more clients, which attract more servers.

Key Takeaways

  • 1MCP is an open standard (created by Anthropic) that standardizes how AI applications connect to external tools and data sources.
  • 2It follows a client-server architecture: hosts contain MCP clients that connect 1:1 with MCP servers.
  • 3Three core primitives: Tools (functions the LLM calls), Resources (data injected into context), and Prompts (reusable templates).
  • 4Two transport layers: stdio for local servers (simple, secure) and Streamable HTTP for remote servers (scalable, standard).
  • 5MCP turns the M x N integration problem into M + N: build one server and it works with every MCP-compatible client.
  • 6The ecosystem is growing rapidly with official servers, community servers, and adoption across major frameworks and applications.

Explore Related Content