LLM Concepts

Model Context Protocol (MCP) Guide

Engineering guide to MCP: host–client–server architecture, tools/resources/prompts, transports, auth, timeouts, and how MCP standardizes tool I/O without replacing orchestration or guardrails.

55 min readIntermediateLast reviewed: 4 August 2026

Quick Summary

MCP is an open, JSON-RPC protocol that standardizes how LLM hosts discover and invoke tools, resources, and prompts from external servers—without owning orchestration, retrieval, or policy.

One Analogy

MCP is USB-C for AI capabilities: one connector shape for many devices, while the host still decides power policy, permissions, and which cable is plugged in.

Engineering Rule

Treat MCP as an inference-time I/O contract between host and capability servers—never as a substitute for orchestration loops, retrieval pipelines, or server-side authorization.

Try the Tool Discovery & Invocation Lab

See how an MCP client initializes with a server, discovers tool contracts through tools/list, and invokes a capability through tools/call across a protocol boundary.

Try Interactive Lab

TL;DR

  • MCP standardizes tool I/O — how hosts discover and invoke tools, resources, and prompts from servers over JSON-RPC. It does not replace orchestration, retrieval, or guardrails.
  • Three primitives: Tools (actions), Resources (read-only context), Prompts (reusable templates). Servers expose them; clients (inside a host) consume them.
  • Host–client–server model: The host (IDE, desktop app, agent runtime) owns consent and the model conversation. Clients talk to servers over a transport; under the 2026-07-28 spec the protocol core is stateless (no session handshake).
  • Inference-time protocol: MCP is not a training method. At request time, the host advertises discovered schemas to the model (often via function calling); the model emits intent; the host forwards approved calls to servers.
  • Transports: stdio for local subprocess servers; Streamable HTTP for remote servers (legacy HTTP+SSE is deprecated with a long offramp). Auth, timeouts, and least privilege remain your responsibility.
  • Vs ad-hoc function calling: Provider function calling is the model-facing intent format inside one app. MCP makes the same capabilities discoverable and reusable across many hosts.

On this page

Why This Matters

Production LLM applications need live data and side effects: issue trackers, databases, internal APIs, filesystems. Without a shared protocol, every host reimplements discovery, schema packaging, auth wiring, and error shapes for every integration. Cursor, Claude Desktop, and a custom agent each grow private adapters for GitHub, Postgres, and Slack. That is an N×M maintenance problem.

MCP collapses the integration surface to N + M: build one server per system; any compatible host can connect. For platform teams, that separation matters more than convenience. Capability ownership moves to the team that owns the backend. Agent and IDE teams consume a stable contract instead of forking SDK wrappers.

MCP also clarifies layer boundaries. Function calling answers “how does the model propose an action?” MCP answers “how does a host talk to an external capability process?” AI agents answer “when and how often should we loop?” Guardrails answer “what is allowed?” Confusing these layers produces brittle systems that treat a protocol handshake as a security model.

Who should read this?

Reader Why
AI / platform engineers Expose internal APIs as reusable MCP servers with auth and SLOs.
Agent / IDE authors Consume servers without per-tool adapters; enforce host-side consent.
Security engineers Map trust boundaries across host, client, and server credentials.
Technical leads Decide MCP vs in-process tools vs deterministic workflows.

The Problem MCP Solves

Before MCP, tool integration looked like this:

  1. Define tool schemas in application code (often duplicated per framework).
  2. Hard-code auth and base URLs inside each agent or IDE plugin.
  3. Translate model tool calls into SDK-specific HTTP or SDK calls.
  4. Repeat for every host that needs the same backend.

Failures were duplicated: inconsistent descriptions, missing timeouts, different error formats, and no shared discovery. When GitHub’s API changed, every adapter changed.

MCP solves the capability interface problem—standardized list/call/read contracts and transports—so hosts and servers can evolve independently.

MCP does not solve:

  • which tools to advertise on a given request;
  • how to plan multi-step work (agent planning);
  • how to retrieve documents (RAG);
  • whether a call is authorized;
  • whether the model selected the right tool;
  • how to evaluate answer quality.

Those remain host, retrieval, and policy concerns.

MCP host-client-server stack

How We Got Here

Early LLM apps prompted models to emit free-form “Action:” lines or JSON in markdown. Frameworks added parsers. Providers then shipped native function calling: typed name + arguments as a first-class response field. That fixed the model↔app boundary inside one process, but every application still built private connectors.

Agent frameworks (LangChain tools, OpenAI Assistants tools, custom registries) reduced boilerplate inside one stack, yet tools remained framework-local. Moving the same Postgres lookup from a Python agent to an IDE required a rewrite.

Anthropic open-sourced the Model Context Protocol in November 2024 as a host-agnostic client–server standard over JSON-RPC 2.0. SDKs in TypeScript and Python, reference servers, and clients (Claude Desktop, Cursor, and others) followed. Later revisions refined HTTP transports (including Streamable HTTP) and capability negotiation. The 2026-07-28 specification made the largest change since launch: a stateless protocol core (no initialize handshake / Mcp-Session-Id), Multi Round-Trip Requests (MRTR) for mid-call input, a formal extensions framework (Tasks, MCP Apps), and stronger OAuth alignment. Pin the protocol and SDK versions you deploy; hosts and servers negotiate version per request.

Diagram: Evolution toward interoperable tools

timeline
    title From ad-hoc tool glue to MCP
    section Prompt era
      Text actions : ReAct-style Action lines
      JSON in prose : Fragile parsers
    section Provider APIs
      Function calling : Typed intents in one app
      Framework tools : LangChain-style registries
    section Interop
      MCP 2024 : Host-agnostic discovery and I/O
      MCP 2026-07-28 : Stateless core + extensions

Structure moved from prose to provider APIs, then capability transport became a shared protocol that can scale on ordinary HTTP infrastructure.

Training versus inference

MCP is an inference-time protocol. Training or post-training may teach a model the general habit of selecting tools and filling arguments. That does not install MCP servers into model weights.

At inference:

  1. The host connects to configured servers and lists tools/resources/prompts.
  2. The host converts those definitions into the provider’s tool schema format (or an internal registry).
  3. The model conditions on the advertised set and may emit a structured call.
  4. The host validates, authorizes, and forwards the call to the MCP server.
  5. The server returns a result; the host continues the conversation.

Disabling a server in host config removes that capability for subsequent requests. No permanent learning occurs from listing tools.

What Is Model Context Protocol?

The Model Context Protocol is an open standard for connecting LLM applications to external data and tools. It defines JSON-RPC methods for listing and invoking tools, reading resources, and fetching prompt templates. Transports carry those messages over stdio (local) or Streamable HTTP (remote). Under 2026-07-28, each request is self-describing: protocol version, client identity, and capabilities ride in _meta (and matching HTTP headers on Streamable HTTP) instead of a prior handshake.

Primitive Purpose Example
Tools Side-effecting or query actions the model may request create_issue, run_query, send_message
Resources Read-only data URI-addressable by the host/model repo://README, schema dumps, API docs
Prompts Named templates with arguments Code-review checklist, incident summary form

An MCP server wraps one domain (GitHub, Postgres, an internal customer API). An MCP client is the protocol peer inside a host application. The host may run many clients—one per server—and presents a unified tool surface to the user and model.

MCP Server (GitHub)          MCP Client (in Cursor)       LLM (via host)
├── tools/                   ├── discover / list          ├── sees tool schemas
│   ├── create_issue         ├── tools/call forward       ├── selects tool
│   └── list_prs             └── consent / policy         └── generates answer
├── resources/
│   └── repo://file/...
└── prompts/
    └── code_review

How MCP Works

Protocol layers

MCP separates:

  • Data / RPC layer — JSON-RPC 2.0 methods (tools/list, tools/call, resources/list, resources/read, prompts/list, optional server/discover, …).
  • Transport layer — how bytes move (stdio pipes vs Streamable HTTP).

On 2026-07-28, there is no required initialize / initialized exchange and no Mcp-Session-Id. Clients may call server/discover when they want capabilities up front; otherwise any request can land on any load-balanced instance. Streamable HTTP requests include MCP-Protocol-Version, Mcp-Method, and Mcp-Name headers so gateways can route without parsing bodies.

Multi Round-Trip Requests (MRTR) replace earlier server-initiated sampling/elicitation streams: a tool can return resultType: "input_required" with requests and opaque state; the client answers and retries the original call. Application state that must span calls should be an explicit tool-returned handle, not hidden transport session state.

Roots, Sampling, and Logging are deprecated with at least a twelve-month removal window; new implementations should avoid them. Tasks live in the formal extensions framework (io.modelcontextprotocol/tasks) rather than experimental core.

Transports

Transport Typical use Characteristics
stdio Local IDE / desktop servers Host spawns subprocess; JSON-RPC on stdin/stdout; process lifetime tied to host config
Streamable HTTP Shared or remote servers Stateless-friendly; needs TLS, auth, timeouts, horizontal scale

Local config often looks like:

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}"
      }
    }
  }
}

Remote production servers add OAuth or service tokens, request timeouts, and health checks. stdio servers are not a multi-tenant API; each client connection is typically one process.

Relationship to function calling

MCP does not replace function calling. The usual composition is:

  1. MCP client discovers tool schemas from servers.
  2. Host maps schemas into the provider’s function/tool definition format.
  3. Model emits a function-call intent.
  4. Host maps the approved intent back to tools/call on the correct MCP server.

Ad-hoc function calling keeps implementations inside one codebase. MCP externalizes implementations behind a protocol so multiple hosts share them. Both still require allowlists, validation, authz, and timeouts on the host (and often again on the server).

Diagram: Model intent meets MCP transport

sequenceDiagram
    actor U as User
    participant H as Host
    participant M as LLM API
    participant C as MCP client
    participant S as MCP server
    U->>H: Authenticated request
    H->>C: tools/list
    C->>S: tools/list (+ _meta / headers)
    S-->>C: Tool schemas (+ cache hints)
    H->>M: Messages + mapped schemas
    M-->>H: Function-call intent
    H->>H: Validate + authorize
    H->>C: tools/call
    C->>S: tools/call + args
    S-->>C: Result or error
    H->>M: Tool result message
    M-->>H: Final answer
    H-->>U: Response

The model proposes; the host polices; MCP only carries the approved call to the server.

Architecture

In a production AI system, MCP sits in the tools / integration layer of AI system architecture. Orchestration still owns routing, budgets, and retries. Retrieval still owns indexes. Guardrails still own policy.

Diagram: Where MCP sits in a production stack

flowchart TB
    subgraph Host["Host application"]
      UI[UI / API]
      ORCH[Orchestrator]
      FC[Function-call adapter]
      GW[Guardrails / policy]
      CLI[MCP clients]
    end
    subgraph Servers["MCP servers"]
      GH[GitHub server]
      DB[DB server]
      INT[Internal API server]
    end
    UI --> ORCH
    ORCH --> FC
    FC --> GW
    GW --> CLI
    CLI --> GH
    CLI --> DB
    CLI --> INT
    ORCH --> RET[Retrieval]
    ORCH --> LLM[LLM provider]

MCP standardizes the edges to external systems; the host remains the control plane.

Typical production layout:

Component Responsibility
Host UX, model provider, consent, tool allowlist, tracing
MCP client RPC, retries, transport errors, protocol version on each request
MCP server Domain tools, credential use, input validation, timeouts to backends
Backends Source of truth (GitHub, DB, CRM)

Step-by-Step Flow

  1. Identify the capability — Which system should be reusable across hosts? Prefer one server per domain.
  2. Define primitives — Tools for actions; resources for schemas/docs; prompts for repeated workflows.
  3. Implement the server — Use the official SDK; validate inputs; set backend timeouts; return structured errors.
  4. Secure credentials — Server-side secrets via env/secret manager, or delegated tokens for multi-tenant hosts; never commit tokens in config.
  5. Configure the host — Register command/URL, env, and which principals may enable the server.
  6. Discover catalogs — Optional server/discover, then tools/list / resources/list / prompts/list (honor ttlMs / cacheScope cache hints when present).
  7. Advertise to the model — Map a bounded subset of tools into the provider request for this turn.
  8. Execute approved calls — Host validates → authorize → tools/call with deadline → sanitize result size; handle MRTR input_required loops if the server asks for mid-call input.
  9. Observe — Log server, tool, latency, outcome codes; evaluate selection quality separately from backend SLOs.
  10. Version — Pin protocol/SDK versions; treat tool schemas as APIs; add fields compatibly; rename for breaking semantics.

Diagram: MCP request lifecycle (2026-07-28)

stateDiagram-v2
    [*] --> Ready: transport configured
    Ready --> Listing: tools/resources/prompts
    Listing --> Ready: catalogs cached
    Ready --> Calling: tools/call
    Calling --> InputNeeded: input_required (MRTR)
    InputNeeded --> Calling: retry with inputResponses
    Calling --> Ready: result or error
    Calling --> Failed: timeout / crash
    Failed --> Ready: retry / backoff policy
    Ready --> [*]

Steady-state list/call loops do not require a protocol session; failures need explicit retry policy. App state spans calls via explicit handles, not transport sessions.

Real Production Example

Pattern: wrap an internal customer API as an MCP server with bearer auth, per-call timeouts, and sanitized errors. Hosts (Cursor, a custom agent) connect via stdio locally or HTTP remotely.

Python MCP server (stdio) with auth and timeouts

# customer_mcp_server.py
from __future__ import annotations

import json
import os
from typing import Any

import httpx
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool

API_BASE = os.environ["CUSTOMER_API_URL"]
API_KEY = os.environ["CUSTOMER_API_KEY"]
# Bound backend latency so one slow CRM call cannot hang the host request.
BACKEND_TIMEOUT_S = float(os.environ.get("MCP_BACKEND_TIMEOUT_S", "8"))

server = Server("customer-api")


def _auth_headers() -> dict[str, str]:
    if not API_KEY:
        raise RuntimeError("CUSTOMER_API_KEY is not configured")
    return {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
        "Accept": "application/json",
    }


@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="lookup_customer",
            description=(
                "Look up a customer by email or customer_id. "
                "Use for account status, plan, and billing contact. "
                "Do not use for placing orders or changing plans."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "email": {
                        "type": "string",
                        "description": "Customer email if known",
                    },
                    "customer_id": {
                        "type": "string",
                        "description": "Stable ID, e.g. CUST-12345",
                    },
                },
                "additionalProperties": False,
            },
        ),
        Tool(
            name="list_recent_orders",
            description=(
                "List recent orders for a customer_id. "
                "Read-only. Prefer limit <= 20."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "customer_id": {"type": "string"},
                    "limit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 50,
                        "default": 10,
                    },
                },
                "required": ["customer_id"],
                "additionalProperties": False,
            },
        ),
    ]


async def _safe_json(response: httpx.Response) -> Any:
    try:
        return response.json()
    except Exception:
        return {"raw": response.text[:2000]}


@server.call_tool()
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
    try:
        async with httpx.AsyncClient(
            base_url=API_BASE,
            headers=_auth_headers(),
            timeout=httpx.Timeout(BACKEND_TIMEOUT_S, connect=3.0),
        ) as client:
            if name == "lookup_customer":
                params = {k: v for k, v in arguments.items() if v}
                if not params:
                    return [
                        TextContent(
                            type="text",
                            text=json.dumps(
                                {"error": "validation_error", "message": "email or customer_id required"}
                            ),
                        )
                    ]
                resp = await client.get("/customers", params=params)
            elif name == "list_recent_orders":
                cid = arguments["customer_id"]
                limit = int(arguments.get("limit", 10))
                resp = await client.get(
                    f"/customers/{cid}/orders",
                    params={"limit": limit},
                )
            else:
                return [
                    TextContent(
                        type="text",
                        text=json.dumps({"error": "unknown_tool", "name": name}),
                    )
                ]

            if resp.status_code == 401:
                payload = {"error": "upstream_unauthorized"}
            elif resp.status_code == 404:
                payload = {"error": "not_found"}
            elif resp.status_code >= 400:
                payload = {
                    "error": "upstream_error",
                    "status": resp.status_code,
                }
            else:
                payload = await _safe_json(resp)

            return [TextContent(type="text", text=json.dumps(payload))]
    except httpx.TimeoutException:
        return [
            TextContent(
                type="text",
                text=json.dumps({"error": "timeout", "timeout_s": BACKEND_TIMEOUT_S}),
            )
        ]
    except Exception:
        # Do not leak stack traces or secrets into the model context.
        return [
            TextContent(
                type="text",
                text=json.dumps({"error": "internal_error"}),
            )
        ]


async def main() -> None:
    async with stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            server.create_initialization_options(),
        )


if __name__ == "__main__":
    import asyncio

    asyncio.run(main())

Host configuration (local)

{
  "mcpServers": {
    "customer-api": {
      "command": "python",
      "args": ["/opt/mcp/customer_mcp_server.py"],
      "env": {
        "CUSTOMER_API_URL": "https://api.internal.acme.com/v1",
        "CUSTOMER_API_KEY": "${CUSTOMER_API_KEY}",
        "MCP_BACKEND_TIMEOUT_S": "8"
      }
    }
  }
}

TypeScript host-side pattern (timeouts + allowlist)

When you build a custom host—not only Cursor—wrap MCP calls with the same controls you use for in-process tools:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

const ALLOWED = new Set(['lookup_customer', 'list_recent_orders']);
const CALL_TIMEOUT_MS = 10_000;

async function callMcpTool(
  client: Client,
  name: string,
  args: Record<string, unknown>,
  signal?: AbortSignal
) {
  if (!ALLOWED.has(name)) {
    throw new Error(`tool_not_allowlisted:${name}`);
  }

  const timeout = AbortSignal.timeout(CALL_TIMEOUT_MS);
  const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;

  return client.callTool({ name, arguments: args }, undefined, {
    signal: combined,
  });
}

async function connectCustomerServer() {
  const transport = new StdioClientTransport({
    command: 'python',
    args: ['/opt/mcp/customer_mcp_server.py'],
    env: {
      ...process.env,
      CUSTOMER_API_URL: process.env.CUSTOMER_API_URL!,
      CUSTOMER_API_KEY: process.env.CUSTOMER_API_KEY!,
    },
  });
  const client = new Client({ name: 'acme-agent', version: '1.0.0' });
  await client.connect(transport);
  return client;
}

Notes for production:

  • Auth: Prefer short-lived tokens scoped to the server’s least privilege. For multi-tenant hosts, do not share one global CRM key across all end users without an authorization check that binds the authenticated user to the customer record. Prefer Client ID Metadata Documents (CIMD) over Dynamic Client Registration for new OAuth clients; DCR is deprecated under 2026-07-28.
  • Timeouts: Set connect + call timeouts on the host and backend timeouts inside the server. Host timeout should be slightly larger than backend timeout so the server can return a structured timeout error.
  • Result size: Truncate large JSON before returning to the model; huge payloads burn context and hide signal.

Design Decisions

Decision Option A Option B Guidance
Server scope One server per system Monolith of all tools Prefer one domain per server for deploy and blast-radius control
Transport stdio HTTP (remote) stdio for local IDE tools; HTTP for shared platforms
Auth placement Server holds secrets Client passes user token Server secrets for single-tenant ops; delegated tokens for multi-tenant
Tool granularity Many fine tools Few coarse tools Coarse tools when selection errors dominate; fine when permissions differ
Resource use Schemas/docs only Live business data Prefer resources for stable context; tools for queries with authz
Error shape Raw HTTP errors Stable error codes Stable codes for model recovery; detailed logs only in telemetry
Host vs server validation Validate once Validate both Validate both: host for policy, server for domain invariants

Common patterns

  • Reference servers for popular systems; thin wrappers around official SDKs.
  • Internal platform servers owned by service teams; versioned like public APIs.
  • Read-only servers in regulated environments; mutations via separate host workflows with human approval.
  • Adapter bridges — LangChain / agent frameworks calling MCP so existing orchestrators reuse servers.

When should I use this?

Use MCP Prefer instead
Standardizing tool/data connections across hosts One-off function schemas for a single app
Sharing tools across Cursor/Claude/custom agents Replacing orchestration or authz
Exposing internal systems as MCP servers Direct model→DB access without an app layer
Multi-client tool ecosystems Skipping timeouts, allow-lists, and audit logs

Comparisons

Approach What it standardizes Execution location Best fit
Ad-hoc function calling Model intent shape inside one app In-process handlers Single product, few tools, tight loop latency
Framework tools (e.g. LangChain) Framework-local tool objects Process running the framework One orchestration stack
MCP Discovery + transport across hosts Separate server process / service Reuse across IDEs, desktops, agents
Deterministic workflow Fixed steps, no model routing Workflow engine Known procedures, high compliance
AI agents Loop, state, stopping rules Orchestrator (may call MCP) Open-ended tasks with bounded budgets

MCP versus function calling

Function calling is the model-facing mechanism. MCP is the capability-facing protocol. Most production hosts use both. Choosing MCP does not remove the need for schemas, allowlists, or authorization described in the function-calling guide.

MCP versus tool calling

Tool calling is the umbrella product concept (model uses external capabilities). MCP is one concrete interoperability standard for packaging those capabilities. You can implement tool calling without MCP; MCP without a model is just an RPC API.

MCP versus orchestration / RAG / guardrails

Concern Owner
Multi-step plans, retries, memory Orchestrator / agents
Document retrieval RAG / retrieval layer
Policy, PII, jailbreak resistance Guardrails
Capability I/O contract MCP

Using MCP does not mean “the protocol will plan, retrieve, and refuse unsafe actions.” Those are separate subsystems that should call or wrap MCP, not be replaced by it.

Common Mistakes

  1. Treating MCP as orchestration — Connecting servers does not plan tasks or stop runaway loops. Budgets and stopping rules stay in the host.
  2. Assuming MCP replaces RAG — Resources are not a vector index. Retrieval pipelines remain separate.
  3. Equating MCP with security — The protocol carries messages; it does not define enterprise IAM. Implement authn/authz yourself.
  4. Exposing 30+ tools from one server — Selection quality collapses; split by domain and allowlist per request.
  5. Vague tool descriptions — Descriptions are inference prompts. State when to use and when not to use each tool.
  6. No timeouts — stdio and HTTP calls can hang forever without deadlines on host and server.
  7. Leaking secrets in results — Upstream 500 bodies and stack traces must not enter the model context.
  8. Hardcoding credentials in committed config — Use secret managers and redacted env injection.
  9. Skipping host allowlists — Discovery is not permission. A listed tool can still be forbidden for a tenant.
  10. Ignoring resources — Dumping schemas via tools wastes turns; expose stable docs as resources.
  11. Not versioning schemas — Breaking argument shapes silently breaks every connected host.
  12. Trusting tool output as instructions — MCP results are untrusted data; apply the same injection discipline as web retrieval.

Where It Breaks Down

  • Protocol and SDK churn — Spec and SDK versions move quickly; pin versions and test upgrades.
  • Incomplete auth standardization — OAuth patterns continue to harden (issuer validation, CIMD); do not assume every host speaks the same auth profile yet.
  • Latency — Extra serialization and process hops (often tens of milliseconds, more on remote HTTP) add up inside tight agent loops.
  • Catalog size — Listing everything does not solve contextual tool routing; large catalogs still hurt models.
  • Stateful backends — Long-lived sockets, interactive TTYs, or multi-hour jobs need explicit handles or the Tasks extension—not hidden transport sessions.
  • Partial SDK feature parity — Not every language SDK exposes every transport or capability at the same time.
  • Operational coupling — A crashing stdio server takes down that local process; remote servers need health checks and backpressure. Stateless HTTP helps horizontal scale but does not remove backend failure modes.

When NOT to Use MCP

Prefer simpler approaches when:

  • a single application owns a few tools and will never share them with other hosts;
  • the operation is a fixed deterministic workflow with no benefit from model-selected tools;
  • sub-10ms in-process latency is required for high-frequency inner loops;
  • you cannot yet enforce auth, audit, and timeouts around a new network/process boundary;
  • the “integration” is only structured extraction with structured outputs and no external I/O;
  • compliance requires a single audited service path that must not spawn user-configured subprocesses.

MCP shines when the same capability must be consumed by IDEs, desktop assistants, and multiple agent runtimes with independent release cycles.

Running in Production

Best Practice

Version each server’s tool schemas, pin SDK/protocol versions, enforce host allowlists, and measure tool-selection quality separately from backend availability.

Warning

MCP servers inherit the privileges of their credentials. A malicious or compromised server config is a confused-deputy risk—validate server provenance and run with least OS and API privilege.

Important

Host deadlines must cover MCP round-trips. A model loop without max turns plus per-call timeouts can amplify one slow server into a cost and latency incident.

Decision Trade-off

Remote HTTP servers enable central ops and scaling but add network failure modes. stdio is simpler for local developer tools but hard to multi-tenant and monitor centrally.

Dimension Practice
Scaling Scale Streamable HTTP servers behind a load balancer (stateless core); treat stdio as one process per local host
Latency Budget MCP overhead in agent SLOs; cache tools/list using catalog cache hints
Cost Protocol is free; cost is tokens for schemas/results plus backend usage
Security TLS for remote, secret rotation, input validation, audit mutations, sandbox where needed
Monitoring Trace host → client → server → backend with shared correlation IDs
Evaluation Golden tasks for correct tool selection across hosts; contract tests for schema changes
HA Health endpoints, graceful drain, retry with backoff for remote transports

Security checklist

  • Advertise only tools needed for the current task class.
  • Authorize using trusted identity, not model-supplied user_id fields.
  • Separate read and write credentials when possible.
  • Cap result payload size before synthesis.
  • See Guardrails for policy hooks and AI system architecture for layer placement.

Interview Questions

What problem does MCP solve that function calling does not?

Function calling standardizes how a model emits intent inside one application. MCP standardizes how hosts discover and invoke capabilities implemented outside that application so multiple clients can share one server.

Is MCP a training technique?

No. It is an inference-time protocol for capability discovery and invocation. Training may improve general tool use; MCP wires live servers at request time.

Does adopting MCP replace the need for guardrails?

No. MCP moves bytes and method calls. Authorization, content policy, PII handling, and injection defenses remain host and platform responsibilities.

How should timeouts be layered?

Set a backend timeout in the server, a slightly larger MCP call timeout in the host, and an overall agent/request budget above that. Return structured timeout errors instead of hanging.

stdio or HTTP for production?

stdio fits local developer tooling and single-user desktop hosts. Shared multi-user or centrally operated capabilities generally need HTTP with auth, TLS, and horizontal scaling.

Where does authorization live?

At minimum in the host (who may enable which server/tool) and in the server (who may access which backend records). Identity must come from trusted session or service credentials, not from model arguments alone.

What changed in the 2026-07-28 MCP specification?

The protocol core became stateless: no initialize handshake and no Mcp-Session-Id. Requests carry version and client metadata; optional server/discover replaces mandatory capability negotiation. MRTR handles mid-call input; Tasks and MCP Apps live in extensions; Roots/Sampling/Logging and legacy HTTP+SSE are deprecated with long offramps.

How do resources differ from tools?

Resources are read-oriented, URI-addressable context. Tools are invoked actions that may query or mutate. Prefer resources for stable documentation and schemas.

What breaks when a server exposes too many tools?

Context cost rises and model selection accuracy falls. Split servers by domain and allowlist a subset per request.

Key Takeaways

  • MCP standardizes tool I/O across hosts and servers; it is not an orchestrator, retriever, or policy engine.
  • Use tools, resources, and prompts deliberately; keep servers domain-scoped.
  • Compose MCP with function calling: discover via MCP, propose via the model, execute only after host checks.
  • The protocol operates at inference time; training does not embed your servers.
  • Production quality hinges on auth, timeouts, allowlists, schema versioning, observability, and pinned protocol versions (2026-07-28+).
  • Prefer MCP when capabilities must be reused across multiple AI clients; prefer in-process tools for single-app, latency-sensitive cases.

FAQs

Is MCP only for Anthropic / Claude?

No. MCP is an open protocol. Any host can implement a client and any team can implement a server. Models are not required to be Anthropic models; the host mediates.

Do I need MCP if I already use function calling?

Not if one application owns a small, private tool set. MCP helps when the same tools must be shared across IDEs, desktops, and agents without rewriting adapters.

How is MCP different from LangChain tools?

LangChain tools are framework-local objects. MCP tools are protocol services any compliant client can call. Frameworks can adapt MCP servers into their tool interfaces.

Can I wrap an internal API as an MCP server?

Yes—that is a primary enterprise use case. Apply the same auth, rate limits, and audit controls you would for any microservice.

Is MCP secure by default?

No. You must add authentication, authorization, input validation, TLS (for remote), and audit logging. Treat servers as privileged microservices.

What is the difference between MCP tools and resources?

Tools perform operations the model requests. Resources expose read-only data for context. Resources reduce unnecessary tool calls for stable documents.

How do I debug MCP servers?

Use MCP Inspector or SDK logging, run the server standalone, and verify tool descriptions against golden prompts. Trace host allowlist decisions separately from server errors.

Can MCP servers call other MCP servers?

Not as a first-class protocol feature. Compose at the host by connecting multiple clients, or have a server call ordinary HTTP APIs internally.

Does MCP replace RAG?

No. RAG indexes and retrieves unstructured evidence. MCP can expose a search tool or document resources, but retrieval architecture remains separate.

Do I still need an initialize handshake?

Not on protocol version 2026-07-28. Older clients/servers may still speak prior revisions—pin and test both sides. Prefer Tier 1 SDKs that advertise 2026-07-28.

Training vs inference—where does MCP apply?

Only at inference (and in ops). Servers are configured and discovered when the host runs, not baked into model weights.

References

Further Reading

Next Topics

Learning Path

Continue Learning

Related Guides

Related companies

  • Anthropic

    Enterprise-first AI company focused on safe, reliable reasoning models.

  • OpenAI

    Commercial foundation model leader.

Related models

  • Claude Sonnet

    Anthropic’s Claude Sonnet 5 tier — best combination of speed and intelligence for most production agents and coding, at lower cost than Opus.

  • Claude Fable

    Anthropic’s Claude Fable 5 — the most capable widely released Claude for long-horizon agents, deep reasoning, and demanding coding workflows. Mythos 5 is the limited-access peer for Project Glasswing.

  • Claude Opus

    Anthropic’s Claude Opus 5 tier for complex agentic coding, enterprise work, long-context analysis, and careful instruction following. Claude Fable 5 sits above Opus for peak widely released capability.

  • GPT-5.6

    OpenAI’s GPT-5.6 family (Sol flagship, Terra balanced, Luna cost-efficient) for complex reasoning, coding, multimodal understanding, and agentic tool use. The gpt-5.6 API alias routes to Sol.

Related Tools

ToolCategoryPurposeWebsiteBest For
Cursor
TrendingAPICloud
codingAI-native code editor with codebase context, multi-file agents, Origin code hosting, cloud-agent Subscriptions, and intelligent model routing for teams.cursor.comAI-native IDE development
LangGraph
FeaturedOpen SourceAPI
frameworksGraph-based orchestration runtime for long-running, stateful agents.langgraph.devMulti-agent orchestration
LangChain
PopularOpen SourceAPI
frameworksFramework for building LLM-powered applications and workflows.langchain.comRAG systems
Claude
Featured
ai productsAnthropic’s conversational AI focused on reliability and safety.claude.aiLong document analysis
Claude Code
TrendingAPICloud
codingTerminal-first coding agent from Anthropic with long-context codebase reasoning, /design artboards, and CLI/IDE/desktop surfaces.claude.aiTerminal-first agentic coding
Smithery
APICloud
mcp serversRegistry and hosting platform for discovering and deploying MCP servers.smithery.aiMCP server discovery
Glama
APICloud
mcp serversMCP server directory and gateway for connecting AI clients to tools.glama.aiMCP server directory
Composio
Open SourceAPI
mcp serversIntegration platform with 250+ tool connectors and managed MCP server hosting.composio.devAgent tool integrations
Cloudflare MCP
APICloud
mcp serversCloudflare-managed MCP servers for Workers, R2, KV, and edge infrastructure.developers.cloudflare.comEdge-deployed MCP
Zapier MCP
APICloud
mcp serversMCP server exposing 8,000+ Zapier app integrations to AI agents.zapier.comAgent access to SaaS apps
GitHub MCP Server
Open SourceAPI
mcp serversOfficial MCP server for GitHub — repos, issues, PRs, and code search for agents.github.comCoding agents with GitHub access
Notion MCP Server
Open SourceAPI
mcp serversMCP server for reading and writing Notion pages, databases, and workspaces.github.comKnowledge base agents
Slack MCP Server
Open SourceAPI
mcp serversMCP server for Slack messaging, channels, and workspace interactions.github.comSlack bot agents
Microsoft MCP Server
Open SourceAPI
mcp serversMCP integrations for Microsoft 365, Teams, and Azure services.github.comEnterprise Microsoft agents