← AI Agents Open Protocols
🤖 AI Agents

Model Context Protocol (MCP): Complete Guide

Source: mortalapps.com
TL;DR
  • The Model Context Protocol (MCP) is an open standard for connecting AI models to external data sources and tools via a unified JSON-RPC 2.0 interface.
  • It eliminates the N-to-M integration problem by allowing any MCP-compliant host (e.g., Claude, IDEs) to interact with any MCP-compliant server without custom glue code.
  • The protocol is critical for production agent systems requiring secure, discoverable, and type-safe access to enterprise data and local environments.
  • Implementation of the 2025-03-26 specification enables stateless Streamable HTTP transports and standardized OAuth 2.1 authorization flows.
  • Tradeoff: While MCP simplifies integration, it introduces network serialization overhead and requires robust error handling for remote tool execution.

Why This Matters

The Model Context Protocol (MCP) addresses the fundamental fragmentation of the AI agent ecosystem. Before MCP, developers building agentic systems were forced to write bespoke integration layers for every combination of LLM provider and data source. This 'N-to-M' problem created significant technical debt, as a tool built for one framework (e.g., LangChain) could not be easily reused in another (e.g., Semantic Kernel) or directly within an LLM's native interface. MCP provides a standardized 'USB-C' for AI context, decoupling the model's reasoning capabilities from the specific implementation details of the tools it uses.

In production environments, ignoring MCP leads to brittle architectures where tool definitions are scattered across multiple repositories, making security auditing and version control nearly impossible. MCP was invented to centralize these definitions into discoverable servers that can be shared across an entire organization. For enterprises, this means a single 'Database MCP Server' can serve a developer's IDE, a customer support chatbot, and an automated data analysis agent simultaneously. The 2025-03-26 specification further matures the protocol by moving away from stateful session IDs in favor of Streamable HTTP, which aligns with modern cloud-native scaling patterns. This shift is essential for high-concurrency agent loops where maintaining long-lived TCP connections or sticky sessions is a significant operational burden. Without MCP, teams face higher maintenance costs and slower deployment cycles as they struggle to keep up with the rapidly evolving landscape of model-specific tool-calling formats.

Core Concepts

The Model Context Protocol is built upon a client-server architecture where the 'Host' (client) initiates requests and the 'Server' provides capabilities. Understanding these core primitives is essential for implementing the 2025-03-26 specification.

  • Host: The application that integrates an LLM and manages the user session (e.g., Claude Desktop, VS Code, or a custom agentic pipeline). It is responsible for orchestrating tool calls and managing the context window.
  • Server: A lightweight process or service that exposes specific capabilities (Tools, Resources, Prompts) to the Host. Servers can run locally via stdio or remotely via HTTP.
  • Transport: The mechanism used to move JSON-RPC messages between Host and Server. The protocol supports stdio for local processes and Server-Sent Events (SSE) or Streamable HTTP for remote connections.
  • Capabilities: During the handshake, both parties negotiate what they can do. Servers might offer tools/list or resources/subscribe, while Hosts might offer sampling (allowing the server to ask the LLM to generate text).
  • Tools: Executable functions that the LLM can invoke. They have a defined input schema (JSON Schema) and return a result that the model can use in its next reasoning step.
  • Resources: Static or dynamic data that the model can read. Unlike tools, resources are typically passive (e.g., a file's content or a database row) and are identified by URIs.
  • Prompts: Pre-defined templates provided by the server that help the host structure interactions for specific tasks, such as 'Analyze this codebase' or 'Debug this error'.

How It Works

The MCP lifecycle follows a strict sequence of negotiation and execution designed to ensure type safety and security across different environments.

1. Transport Establishment

The Host starts the Server process (for stdio) or connects to a remote endpoint (for SSE/HTTP). In the 2025-03-26 spec, remote connections utilize Streamable HTTP, which allows for bidirectional communication without the overhead of traditional WebSockets. The Host sends an initial connection request to establish the communication channel.

2. The Handshake (Initialize)

Before any data is exchanged, the Host sends an initialize request. This request contains the Host's version, capabilities, and client information. The Server responds with its own capabilities (e.g., whether it supports tools, resources, or prompts) and its protocol version. If the versions are incompatible, the connection is terminated here. In the latest spec, this phase also includes the negotiation of authentication parameters via OAuth 2.1 if required.

3. Discovery Phase

Once initialized, the Host typically calls tools/list and resources/list. The Server returns a list of available tools, including their names, descriptions, and JSON Schema definitions for arguments. This information is injected into the LLM's context window, enabling the model to understand what actions it can take.

4. The Request-Response Loop

When the LLM decides to use a tool, the Host sends a tools/call request to the Server. The Server executes the logic (e.g., querying a database or running a script) and returns a tool/call/result. If the tool execution fails, the Server must return a structured error object within the JSON-RPC response rather than a generic transport-level error (like HTTP 500), allowing the LLM to attempt self-correction.

5. Notifications and Subscriptions

Servers can send asynchronous notifications to the Host, such as notifications/resources/updated if a resource the Host is watching changes. This allows for reactive agent behavior without constant polling. The Host can also send notifications to the Server, such as notifications/initialized to signal that it is ready to start the main loop.

Architecture

The MCP architecture is a layered system designed for transport independence and strict schema adherence. At the base is the Transport Layer, which handles the raw byte stream. Above this is the Message Layer, which implements the JSON-RPC 2.0 protocol, ensuring every message has an ID, a method, and parameters. The Abstraction Layer defines the MCP-specific primitives: Tools, Resources, and Prompts. Execution starts at the Host Application, which acts as the orchestrator. Data flows from the Host to the MCP Server via the Transport. The Server processes the request, potentially interacting with External Systems (Databases, APIs, Local Filesystems), and returns the result back up the stack. In the 2025-03-26 specification, the architecture emphasizes a Stateless Execution Model for HTTP transports, where authentication and context are passed via standardized headers or tokens rather than persistent session identifiers, facilitating easier horizontal scaling of MCP server clusters.

JSON-RPC 2.0 Specification Adherence

MCP utilizes JSON-RPC 2.0 for all communications. Every request must include a unique id (string or integer), the jsonrpc: "2.0" version string, a method name, and a params object. Responses must include the same id and either a result or an error object. This strict structure allows for easy debugging and the use of standard JSON-RPC libraries across different programming languages.

Field Type Description
jsonrpc string Must be exactly "2.0"
id string/int Unique identifier for the request/response pair
method string The MCP method being called (e.g., tools/call)
params object Method-specific parameters

The 2025-03-26 Transport Evolution

The most significant change in the 2025-03-26 specification is the formalization of Streamable HTTP and the deprecation of the Mcp-Session-Id header. Previously, remote MCP required stateful tracking of sessions at the transport level. The new spec moves toward a stateless model where each request is self-contained or uses standard OAuth 2.1 tokens for identity. Streamable HTTP uses a specialized POST-based protocol where the response body remains open for server-to-client notifications, effectively providing a lightweight alternative to WebSockets that is more compatible with corporate firewalls and load balancers.

Tool Schema Design (JSON Schema 2020-12)

MCP tools are defined using JSON Schema 2020-12. This allows for complex validation logic, including nested objects, arrays, and enums. A well-designed tool schema is critical for LLM performance; if the schema is too vague, the model may generate invalid arguments.

{
  "name": "get_weather",
  "description": "Get the current weather for a location",
  "inputSchema": {
    "type": "object",
    "properties": {
      "location": { "type": "string" },
      "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
    },
    "required": ["location"]
  }
}

Resource Templates and URI Schemes

Resources are identified by URIs. MCP supports Resource Templates, which allow servers to expose parameterized data sources. For example, a server might expose postgres://{database}/{table}. The Host can then resolve these templates by providing the necessary variables. This is more efficient than listing every individual table as a separate resource. Servers must provide a listChanged capability if they want the Host to know when the available resources have changed dynamically.

Capability Negotiation Matrix

During the initialize phase, both the client and server exchange a capabilities object. This determines which features are active for the duration of the connection.

Capability Provider Description
tools Server Supports tool discovery and execution
resources Server Supports reading static/dynamic data
prompts Server Provides pre-defined prompt templates
logging Server Can send log messages to the host
sampling Host Allows the server to request LLM completions
roots Host Informs the server of the accessible filesystem paths

Security and OAuth 2.1 Integration

With the removal of custom session management, the 2025-03-26 spec standardizes on OAuth 2.1 for remote server authorization. Servers can specify an auth requirement in their metadata. The Host is responsible for performing the OAuth flow (e.g., Authorization Code Flow with PKCE) and attaching the resulting Bearer token to subsequent MCP requests. This ensures that agentic tools respect the same identity and access management (IAM) policies as human users.

Error Handling and Protocol Codes

MCP defines specific error codes to help the Host distinguish between transport failures, protocol violations, and tool execution errors.

  • -32700: Parse error (Invalid JSON)
  • -32600: Invalid Request (Not a valid JSON-RPC object)
  • -32601: Method not found
  • -32602: Invalid params (Schema validation failed)
  • -32603: Internal error
  • -32000 to -32099: Reserved for MCP-specific protocol errors

When a tool fails, the server should return a successful JSON-RPC response where the result object contains isError: true and the error message in the content array. This allows the LLM to see the error as part of its conversation history and potentially fix the input.

Code Example

A production-ready MCP server in Python using the FastMCP SDK, demonstrating tool definition with type hints and error handling.
Python
import os
import logging
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server with metadata
mcp = FastMCP("Enterprise-Data-Service")

# Configure logging for production observability
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mcp-server")

@mcp.tool()
def query_customer_db(customer_id: str) -> str:
    """Queries the internal database for customer records by ID."""
    # Access secrets from environment variables
    db_url = os.getenv("DATABASE_URL")
    if not db_url:
        logger.error("DATABASE_URL not set")
        return "Error: Database connection not configured."

    try:
        # Simulated database logic
        logger.info(f"Fetching record for: {customer_id}")
        if customer_id == "123":
            return "Customer: Alice, Status: Active, Tier: Gold"
        return f"No record found for ID: {customer_id}"
    except Exception as e:
        logger.exception("Database query failed")
        return f"Error executing query: {str(e)}"

if __name__ == "__main__":
    # Run using stdio transport by default
    mcp.run()
Expected Output
The server starts and waits for JSON-RPC input on stdin. When queried with tools/list, it returns the query_customer_db schema.
Example of a JSON-RPC 2.0 'tools/call' request and a successful response following the MCP specification.
Json
// Request from Host to Server
{
  "jsonrpc": "2.0",
  "id": "req_8821",
  "method": "tools/call",
  "params": {
    "name": "query_customer_db",
    "arguments": {
      "customer_id": "123"
    }
  }
}

// Response from Server to Host
{
  "jsonrpc": "2.0",
  "id": "req_8821",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Customer: Alice, Status: Active, Tier: Gold"
      }
    ],
    "isError": false
  }
}
Expected Output
A valid JSON-RPC response containing the tool execution result formatted for LLM consumption.

Key Takeaways

MCP standardizes the interface between AI hosts and data/tool servers using JSON-RPC 2.0.
The 2025-03-26 specification moves the protocol toward statelessness via Streamable HTTP.
Capability negotiation during the handshake ensures compatibility between hosts and servers.
Tool definitions use JSON Schema 2020-12, requiring precise descriptions for LLM accuracy.
Security is managed through OAuth 2.1 and strict input validation at the tool boundary.
Caching via ttlMs and cacheScope is essential for reducing latency in production agent loops.
MCP enables a 'write once, run anywhere' model for AI tools across different frameworks and IDEs.

Frequently Asked Questions

What is the difference between MCP and OpenAI's Function Calling? +
OpenAI Function Calling is a model-specific feature for defining tools. MCP is a provider-agnostic protocol that standardizes how those tools are discovered, transported, and executed across any model or host.
When should I use stdio vs HTTP transport for MCP? +
Use stdio for local tools running on the same machine as the host (e.g., IDE plugins). Use HTTP for remote enterprise data sources, microservices, or when scaling across a cluster.
Can MCP servers be written in languages other than Python? +
Yes. While Python and TypeScript have the most mature SDKs, any language that can handle JSON-RPC 2.0 over stdio or HTTP can implement an MCP server.
How does MCP handle long-running tool executions? +
MCP is inherently request-response. For very long tasks, the tool should return an 'in-progress' status or a job ID, and the host should use a separate resource or notification to track completion.
What happens if an MCP server and host have different protocol versions? +
During the `initialize` handshake, they compare versions. If they are incompatible (e.g., major version mismatch), the connection fails immediately with a version mismatch error.
Is MCP secure enough for sensitive financial data? +
Yes, provided you implement OAuth 2.1, use TLS for all HTTP traffic, and apply strict RBAC at the tool level. MCP itself is a transport protocol; security depends on your implementation.
How do I debug an MCP connection? +
Most hosts provide an MCP inspector or log window. You can also use tools like `mcp-get` or simple stdio redirection to capture the JSON-RPC traffic between the host and server.
Does MCP support streaming tool outputs? +
As of the 2025-03-26 spec, tool results are typically returned as a single block. However, the Streamable HTTP transport allows for server-sent notifications to update the host on progress.