What is MCP? A Developer Guide to Model Context Protocol

TL;DR — MCP (Model Context Protocol) is an open protocol by Anthropic for connecting LLMs to external data and tools. MCP Spec: "Open protocol for seamless integration between LLM applications and external data sources and tools. Standardized way to connect LLMs with context. Inspired by Language Server Protocol." MCP Architecture: "Client-server architecture. Host (Claude Code, Claude Desktop) creates one client per server. Stdio for local, Streamable HTTP for remote. Tools, resources, prompts as core primitives." MCP Spec Architecture: "Client-host-server architecture. Host creates and manages multiple client instances, controls permissions, enforces security, coordinates AI integration. Capability-based negotiation during initialization." MCP Transports: "Two transports: stdio (local, standard I/O) and Streamable HTTP (remote, HTTP POST + optional SSE). Replaces HTTP+SSE from 2024-11-05." MCP Overview: "JSON-RPC 2.0 messages. Base protocol, lifecycle management, authorization, server features, client features. All implementations MUST support base protocol and lifecycle." Learn more with build MCP server, build MCP client, Claude Desktop MCP, and MCP transports.

MCP Specification defines the protocol: "Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you are building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need."

MCP Architecture explains the design: "MCP follows a client-server architecture where an MCP host — an AI application like Claude Code or Claude Desktop — establishes connections to one or more MCP servers. The MCP host accomplishes this by creating one MCP client for each MCP server. Each MCP client maintains a dedicated connection with its corresponding MCP server."

MCP Architecture

flowchart TD subgraph Host["MCP Host (AI Application)"] Claude["Claude Desktop / Claude Code
or custom AI app"] Client1["MCP Client 1"] Client2["MCP Client 2"] Client3["MCP Client 3"] Coordinator["Host Coordinator
- manages client instances
- enforces security policies
- handles authorization
- coordinates AI integration
- aggregates context"] end subgraph Local["Local MCP Servers (stdio)"] FS["Filesystem Server
stdio transport
single client"] Git["Git Server
stdio transport
single client"] end subgraph Remote["Remote MCP Servers (HTTP)"] Sentry["Sentry Server
Streamable HTTP
many clients"] Slack["Slack Server
Streamable HTTP
many clients"] end subgraph Primitives["Server Primitives"] Tools["Tools
executable functions
file ops, API calls, DB queries"] Resources["Resources
contextual data
file contents, DB records"] Prompts["Prompts
reusable templates
system prompts, few-shot"] end subgraph ClientFeatures["Client Features"] Sampling["Sampling
server-initiated LLM interactions
user must approve"] Roots["Roots
filesystem boundaries
URI limits"] Elicitation["Elicitation
request user info
server-initiated"] end Claude --> Coordinator Coordinator --> Client1 Coordinator --> Client2 Coordinator --> Client3 Client1 -->|stdio| FS Client2 -->|stdio| Git Client3 -->|Streamable HTTP| Sentry Client3 -->|Streamable HTTP| Slack FS --> Tools FS --> Resources FS --> Prompts Sentry --> Tools Sentry --> Resources Client1 --> Sampling Client2 --> Roots Client3 --> Elicitation

MCP Component Comparison

Component Role Provided By Examples Transport
Host Container and coordinator AI application Claude Desktop, Claude Code N/A
Client Connection manager Host (one per server) Isolated session per server N/A
Server Context and capabilities External service Filesystem, Git, Sentry, Slack stdio or HTTP
Tools Executable functions Server File ops, API calls, DB queries Both
Resources Contextual data Server File contents, DB records, API responses Both
Prompts Reusable templates Server System prompts, few-shot examples Both
Sampling LLM interactions Client (offered to server) Server requests LLM completion Both
Roots Filesystem boundaries Client URI limits for server operations Both
Elicitation User info requests Client Server asks user for input Both

Transport Comparison

Transport Use Case Connection Clients Auth Performance
stdio Local servers Standard I/O streams Single client Environment credentials Optimal, no network overhead
Streamable HTTP Remote servers HTTP POST + optional SSE Many clients OAuth, bearer tokens, API keys Network-dependent, supports streaming

Implementation

from dataclasses import dataclass
from typing import Optional
from enum import Enum

class MCPPrimitive(Enum):
    TOOLS = "tools"
    RESOURCES = "resources"
    PROMPTS = "prompts"

class MCPTransport(Enum):
    STDIO = "stdio"
    STREAMABLE_HTTP = "streamable_http"

class MCPClientFeature(Enum):
    SAMPLING = "sampling"
    ROOTS = "roots"
    ELICITATION = "elicitation"

@dataclass
class MCPDeveloperGuide:
    """Developer guide for Model Context Protocol."""

    def get_protocol_overview(self) -> dict:
        """MCP protocol overview."""
        return {
            "name": "Model Context Protocol",
            "type": "Open protocol",
            "created_by": "Anthropic",
            "specification": "2025-11-25 (latest)",
            "message_format": "JSON-RPC 2.0",
            "architecture": "client-host-server",
            "inspiration": (
                "Language Server Protocol (LSP) — "
                "standardizes how to integrate context "
                "and tools into AI applications, similar "
                "to how LSP standardizes language support "
                "across development tools."
            ),
            "purpose": [
                "Share contextual information with language models",
                "Expose tools and capabilities to AI systems",
                "Build composable integrations and workflows",
            ],
            "protocol_layers": [
                "Base Protocol: Core JSON-RPC message types",
                "Lifecycle Management: initialization, "
                "capability negotiation, session control",
                "Authorization: auth framework for HTTP transports",
                "Server Features: resources, prompts, tools",
                "Client Features: sampling, roots, elicitation",
                "Utilities: logging, argument completion, "
                "progress tracking, cancellation",
            ],
        }

    def get_architecture_roles(self) -> dict:
        """MCP architecture roles."""
        return {
            "host": {
                "role": "Container and coordinator",
                "examples": ["Claude Desktop", "Claude Code",
                             "custom AI applications"],
                "responsibilities": [
                    "Creates and manages multiple client instances",
                    "Controls client connection permissions "
                    "and lifecycle",
                    "Enforces security policies and consent "
                    "requirements",
                    "Handles user authorization decisions",
                    "Coordinates AI/LLM integration and sampling",
                    "Manages context aggregation across clients",
                ],
            },
            "client": {
                "role": "Connection manager (one per server)",
                "created_by": "Host",
                "responsibilities": [
                    "Establishes one stateful session per server",
                    "Handles protocol negotiation and "
                    "capability exchange",
                    "Routes protocol messages bidirectionally",
                    "Manages subscriptions and notifications",
                    "Maintains security boundaries between servers",
                ],
            },
            "server": {
                "role": "Context and capabilities provider",
                "examples": ["Filesystem", "Git", "Sentry",
                             "Slack", "database", "API"],
                "responsibilities": [
                    "Expose resources, tools, and prompts "
                    "via MCP primitives",
                    "Operate independently with focused "
                    "responsibilities",
                    "Request sampling through client interfaces",
                    "Must respect security constraints",
                    "Can be local processes or remote services",
                ],
            },
        }

    def get_primitives(self) -> dict:
        """MCP server primitives."""
        return {
            "tools": {
                "description": (
                    "Executable functions that AI applications "
                    "can invoke to perform actions"
                ),
                "examples": [
                    "File operations (read, write, search)",
                    "API calls (GitHub, Slack, Sentry)",
                    "Database queries (SQL, NoSQL)",
                    "Code execution (shell, scripts)",
                ],
                "server_capability": "tool support",
                "usage": (
                    "Client calls tool via JSON-RPC request. "
                    "Server executes and returns result."
                ),
            },
            "resources": {
                "description": (
                    "Data sources that provide contextual "
                    "information to AI applications"
                ),
                "examples": [
                    "File contents",
                    "Database records",
                    "API responses",
                    "Configuration data",
                ],
                "server_capability": "resource subscriptions",
                "usage": (
                    "Client reads resource via URI. "
                    "Server returns content. "
                    "Supports subscriptions for updates."
                ),
            },
            "prompts": {
                "description": (
                    "Reusable templates that help structure "
                    "interactions with language models"
                ),
                "examples": [
                    "System prompts for specific tasks",
                    "Few-shot examples",
                    "Workflow templates",
                    "Structured output templates",
                ),
                "server_capability": "prompt templates",
                "usage": (
                    "Client requests prompt by name. "
                    "Server returns templated message "
                    "with arguments filled in."
                ),
            },
        }

    def get_client_features(self) -> dict:
        """MCP client features offered to servers."""
        return {
            "sampling": {
                "description": (
                    "Server-initiated agentic behaviors and "
                    "recursive LLM interactions"
                ),
                "security": (
                    "Users must explicitly approve any LLM "
                    "sampling requests. Users control: "
                    "whether sampling occurs, the actual "
                    "prompt sent, what results server can see. "
                    "Protocol intentionally limits server "
                    "visibility into prompts."
                ),
                "client_capability": "sampling support",
            },
            "roots": {
                "description": (
                    "Server-initiated inquiries into URI or "
                    "filesystem boundaries to operate in"
                ),
                "usage": (
                    "Client provides roots (URIs) to server. "
                    "Server operates within those boundaries."
                ),
                "client_capability": "roots support",
            },
            "elicitation": {
                "description": (
                    "Server-initiated requests for additional "
                    "information from users"
                ),
                "usage": (
                    "Server requests user input through client. "
                    "Client presents to user and returns response."
                ),
                "client_capability": "elicitation support",
            },
        }

    def get_transports(self) -> dict:
        """MCP transport mechanisms."""
        return {
            "stdio": {
                "description": (
                    "Standard input/output streams for direct "
                    "process communication"
                ),
                "use_case": "Local servers on same machine",
                "clients": "Single client (typically)",
                "auth": (
                    "Retrieve credentials from environment. "
                    "Should NOT use HTTP auth specification."
                ),
                "performance": "Optimal, no network overhead",
                "encoding": "UTF-8 JSON-RPC messages",
            },
            "streamable_http": {
                "description": (
                    "HTTP POST for client-to-server messages "
                    "with optional Server-Sent Events (SSE) "
                    "for streaming"
                ),
                "use_case": "Remote servers",
                "clients": "Many clients",
                "auth": (
                    "OAuth recommended for auth tokens. "
                    "Supports bearer tokens, API keys, "
                    "custom headers."
                ),
                "endpoint": (
                    "Single HTTP endpoint (e.g., "
                    "https://example.com/mcp) supporting "
                    "POST and GET methods"
                ),
                "replaces": "HTTP+SSE transport from "
                            "protocol version 2024-11-05",
                "features": [
                    "HTTP POST for JSON-RPC requests",
                    "Optional SSE for streaming responses",
                    "HTTP GET for server-to-client stream",
                    "Session management",
                    "Resumability and redelivery",
                ],
            },
        }

    def get_capability_negotiation(self) -> dict:
        """MCP capability negotiation process."""
        return {
            "when": "During Initialize handshake",
            "process": [
                "Client sends Initialize request with "
                "its capabilities",
                "Server responds with its capabilities",
                "Both parties agree on available features",
                "Only advertised features can be used "
                "during session",
                "Additional capabilities negotiated through "
                "extensions",
            ],
            "server_capabilities": [
                "Resource subscriptions",
                "Tool support",
                "Prompt templates",
            ],
            "client_capabilities": [
                "Sampling support",
                "Notification handling",
                "Roots support",
                "Elicitation support",
            ],
            "rules": [
                "Implemented server features must be "
                "advertised in capabilities",
                "Emitting resource subscription notifications "
                "requires server to declare subscription support",
                "Tool invocation requires server to declare "
                "tool capabilities",
                "Sampling requires client to declare support "
                "in its capabilities",
                "Both parties must respect declared capabilities "
                "throughout session",
            ],
        }

    def get_security(self) -> dict:
        """MCP security considerations."""
        return {
            "principle": (
                "MCP enables powerful capabilities through "
                "arbitrary data access and code execution. "
                "With this power comes important security "
                "and trust considerations."
            ),
            "host_controls": [
                "Client connection permissions and lifecycle",
                "Security policies and consent requirements",
                "User authorization decisions",
                "AI/LLM integration and sampling coordination",
                "Context aggregation across clients",
            ],
            "sampling_security": [
                "Users must explicitly approve LLM sampling",
                "Users control whether sampling occurs",
                "Users control the actual prompt sent",
                "Users control what results server can see",
                "Protocol intentionally limits server "
                "visibility into prompts",
            ],
            "transport_security": {
                "stdio": "Retrieve credentials from environment",
                "http": "Use OAuth for auth tokens, "
                        "bearer tokens, API keys",
            },
            "server_constraints": [
                "Must respect security constraints",
                "Operate within declared capabilities",
                "Maintain security boundaries",
            ],
        }

    def get_lifecycle(self) -> dict:
        """MCP connection lifecycle."""
        return {
            "phases": [
                {
                    "phase": "Initialize",
                    "description": (
                        "Client sends Initialize request. "
                        "Capability negotiation occurs. "
                        "Server responds with capabilities."
                    ),
                },
                {
                    "phase": "Operation",
                    "description": (
                        "Normal message exchange. "
                        "Requests, responses, notifications. "
                        "Tools invoked, resources read, "
                        "prompts used."
                    ),
                },
                {
                    "phase": "Shutdown",
                    "description": (
                        "Client or server initiates shutdown. "
                        "Session terminated. "
                        "Resources cleaned up."
                    ),
                },
            ],
            "message_types": [
                "Requests: client to server or vice versa, "
                "initiate operation",
                "Responses: reply to requests, containing "
                "result or error",
                "Notifications: one-way message, receiver "
                "MUST NOT send response",
            ],
        }

MCP Developer Checklist

  • [ ] MCP = Model Context Protocol, open protocol by Anthropic
  • [ ] Enables seamless integration between LLM applications and external data sources and tools
  • [ ] Inspired by Language Server Protocol (LSP) — standardizes context and tool integration for AI
  • [ ] Uses JSON-RPC 2.0 message format for all communication
  • [ ] Architecture: client-host-server where host creates one client per server
  • [ ] Host = AI application (Claude Desktop, Claude Code, custom apps)
  • [ ] Host responsibilities: manage client instances, enforce security, handle authorization, coordinate AI integration, aggregate context
  • [ ] Client = connection manager, one per server, maintains isolated session
  • [ ] Client responsibilities: protocol negotiation, message routing, subscriptions, security boundaries
  • [ ] Server = context and capabilities provider (Filesystem, Git, Sentry, Slack, database, API)
  • [ ] Server responsibilities: expose primitives, operate independently, respect security constraints
  • [ ] Servers can be local (stdio) or remote (Streamable HTTP)
  • [ ] Three server primitives: Tools, Resources, Prompts
  • [ ] Tools: executable functions AI can invoke (file ops, API calls, database queries)
  • [ ] Resources: data sources providing contextual information (file contents, DB records, API responses)
  • [ ] Prompts: reusable templates for structuring LLM interactions (system prompts, few-shot examples)
  • [ ] Three client features: Sampling, Roots, Elicitation
  • [ ] Sampling: server-initiated LLM interactions — users must explicitly approve
  • [ ] Roots: filesystem boundaries — server operates within URI limits
  • [ ] Elicitation: server requests additional info from users through client
  • [ ] Two transports: stdio (local) and Streamable HTTP (remote)
  • [ ] stdio: standard I/O streams, single client, environment credentials, optimal performance
  • [ ] Streamable HTTP: HTTP POST + optional SSE, many clients, OAuth auth, supports streaming
  • [ ] Streamable HTTP replaces HTTP+SSE from protocol version 2024-11-05
  • [ ] HTTP endpoint: single path (e.g., https://example.com/mcp) supporting POST and GET
  • [ ] Capability negotiation during Initialize handshake
  • [ ] Servers declare: resource subscriptions, tool support, prompt templates
  • [ ] Clients declare: sampling support, notification handling, roots, elicitation
  • [ ] Only advertised features can be used during session
  • [ ] Both parties must respect declared capabilities throughout session
  • [ ] Protocol layers: Base Protocol, Lifecycle Management, Authorization, Server Features, Client Features, Utilities
  • [ ] All implementations MUST support base protocol and lifecycle management
  • [ ] Other components MAY be implemented based on application needs
  • [ ] Message types: Requests (initiate operation), Responses (result or error), Notifications (one-way)
  • [ ] Connection lifecycle: Initialize → Operation → Shutdown
  • [ ] Security: MCP enables arbitrary data access and code execution — careful security required
  • [ ] Host controls connection permissions, security policies, authorization decisions
  • [ ] Sampling security: users must approve, control prompt, control server visibility
  • [ ] Protocol intentionally limits server visibility into prompts
  • [ ] stdio auth: retrieve credentials from environment, NOT HTTP auth
  • [ ] HTTP auth: OAuth recommended, bearer tokens, API keys, custom headers
  • [ ] Servers must respect security constraints and declared capabilities
  • [ ] Clients maintain security boundaries between servers
  • [ ] Protocol is transport-agnostic — custom transports possible if JSON-RPC preserved
  • [ ] _meta property reserved for additional metadata
  • [ ] TypeScript schema is source of truth, JSON Schema auto-generated
  • [ ] Latest specification: 2025-11-25
  • [ ] Read build MCP server for server implementation
  • [ ] Read build MCP client for client implementation
  • [ ] Read Claude Desktop MCP for desktop integration
  • [ ] Read MCP transports for transport details
  • [ ] Read MCP tools for tool development
  • [ ] Test: Initialize handshake exchanges capabilities correctly
  • [ ] Test: Tools execute and return results via JSON-RPC
  • [ ] Test: Resources read via URI and return content
  • [ ] Test: Prompts return templated messages with arguments
  • [ ] Test: stdio transport communicates via standard I/O
  • [ ] Test: Streamable HTTP handles POST and optional SSE
  • [ ] Test: Capability negotiation restricts to advertised features
  • [ ] Document architecture, primitives, transport choice, and security policies

FAQ

What is the Model Context Protocol (MCP)?

MCP is an open protocol that enables seamless integration between LLM applications and external data sources and tools. MCP Spec: "Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you are building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need. MCP takes inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across development tools. In a similar way, MCP standardizes how to integrate additional context and tools into the ecosystem of AI applications." MCP Architecture: "MCP follows a client-server architecture where an MCP host — an AI application like Claude Code or Claude Desktop — establishes connections to one or more MCP servers. The MCP host creates one MCP client for each MCP server." Protocol uses JSON-RPC 2.0 messages for communication.

What are the core components of MCP architecture?

MCP has a client-host-server architecture with three core primitives: tools, resources, and prompts. MCP Architecture: "MCP follows a client-server architecture where an MCP host establishes connections to one or more MCP servers. Each MCP client maintains a dedicated connection with its corresponding server." MCP Spec: "MCP follows a client-host-server architecture where each host can run multiple client instances. The host process acts as container and coordinator: creates and manages multiple client instances, controls connection permissions, enforces security policies, handles authorization, coordinates AI/LLM integration, manages context aggregation." Core primitives: (1) Tools: executable functions AI can invoke (file operations, API calls, database queries). (2) Resources: data sources providing contextual information (file contents, database records, API responses). (3) Prompts: reusable templates for structuring LLM interactions. Client features: Sampling (server-initiated LLM interactions), Roots (filesystem boundaries), Elicitation (request user info).

What transports does MCP support?

MCP defines two standard transports: stdio for local process communication and Streamable HTTP for remote server communication. MCP Transports: "The protocol currently defines two standard transport mechanisms: stdio, communication over standard in and standard out, and Streamable HTTP. This replaces the HTTP+SSE transport from protocol version 2024-11-05." MCP Architecture: "Stdio transport uses standard input/output streams for direct process communication between local processes on the same machine, providing optimal performance with no network overhead. Streamable HTTP transport uses HTTP POST for client-to-server messages with optional Server-Sent Events for streaming. Enables remote server communication and supports standard HTTP authentication including bearer tokens, API keys, and custom headers. MCP recommends using OAuth to obtain authentication tokens." Local MCP servers using STDIO typically serve a single client. Remote MCP servers using Streamable HTTP serve many clients.

How does capability negotiation work in MCP?

MCP uses a capability-based negotiation system where clients and servers explicitly declare supported features during initialization. MCP Spec: "The Model Context Protocol uses a capability-based negotiation system where clients and servers explicitly declare their supported features during initialization. Capabilities determine which protocol features and primitives are available during a session. Servers declare capabilities like resource subscriptions, tool support, and prompt templates. Clients declare capabilities like sampling support and notification handling. Both parties must respect declared capabilities throughout the session." Capability negotiation: (1) During Initialize handshake, client and server exchange capabilities. (2) Server advertises: resources, tools, prompts, subscriptions. (3) Client advertises: sampling, roots, elicitation. (4) Only advertised features can be used during session. (5) Additional capabilities can be negotiated through extensions. This ensures clear understanding of supported functionality while maintaining protocol extensibility.

What security considerations does MCP require?

MCP enables powerful capabilities through arbitrary data access and code execution, requiring careful security and trust considerations. MCP Spec: "The Model Context Protocol enables powerful capabilities through arbitrary data access and code execution paths. With this power comes important security and trust considerations that all implementors must carefully address. Users must explicitly approve any LLM sampling requests. Users should control: whether sampling occurs at all, the actual prompt that will be sent, what results the server can see. The protocol intentionally limits server visibility into prompts." MCP Architecture: "The transport layer manages communication channels and authentication between clients and servers. It handles connection establishment, message framing, and secure communication." Security: (1) Host controls client connection permissions and lifecycle. (2) Host enforces security policies and consent requirements. (3) Host handles user authorization decisions. (4) STDIO transport: retrieve credentials from environment, not HTTP auth. (5) HTTP transport: use OAuth for authentication tokens. (6) Servers must respect security constraints. (7) Clients maintain security boundaries between servers.


Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →