Back to Hub
Certification Study Notes

Model Context Protocol (MCP)

Vendor-neutral guide to the open protocol that connects AI applications to tools and context: host/client/server roles, when to use tools vs. resources vs. prompts, resource catalogs, tool descriptions as the selection signal, JSON-RPC error codes vs. isError tool execution errors, stdio vs. Streamable HTTP, project vs. user server scoping, OAuth 2.1 and the MCP threat model. Spec baseline: revision 2026-07-28.

Author: Michaël Bettan
Track: Foundation / Agent Architecture & Protocols
Test Yourself: MCP Practice Quiz →
00

Overview: What MCP Is & Why It Exists

Definition

The Model Context Protocol (MCP) is an open, vendor-neutral protocol that standardizes how AI applications discover and use external tools, data (resources) and prompt templates. Messages are JSON-RPC 2.0 over a pluggable transport (stdio or Streamable HTTP). Analogy: a "USB-C port for AI". It defines the connector, not what the device does with it.

The clean interview line

MCP standardizes how AI applications connect to tools and context. It doesn't determine how your agent reasons or orchestrates those tools. Started at Anthropic (Nov 2024), it's now governed by the Agentic AI Foundation under the Linux Foundation and implemented across hosts, frameworks and clouds.

Problem before MCPWhat MCP changes
Every data source needed its own custom integration per AI appWrite a server once; every MCP host can use it
M × N integration matrix (M apps × N tools)M + N: each app implements a client once, each tool a server once
Tool schemas, auth and transport reinvented per vendorShared discovery (*/list), invocation (tools/call), transports and OAuth profile
User context (files, tickets, DBs) hard to bring to the model safelyStandard resources and consent hooks owned by the host
WhenMilestone (know the shape)
Nov 2024Open-sourced with spec, SDKs and reference servers
2025Streamable HTTP replaces HTTP+SSE (2025-03-26); OAuth 2.1 profile matures (2025-06-18); tasks, server identity, extensions (2025-11-25)
Late 2025Donated to the Agentic AI Foundation (Linux Foundation)
2026-07-28Stateless revision: no initialize handshake, per-request _meta, server/discover; roots, sampling and logging deprecated

Common trap: "MCP is one vendor's plug-in format"

No. It's an open spec under neutral governance. Any compliant host (chat apps, IDEs, coding CLIs, agent frameworks) can use any compliant server, with any model.

01

1. Architecture: Host, Client, Server

RoleWhat it isResponsibilitiesExamples
HostThe AI application the user interacts withRuns the model and agent loop; creates clients; owns consent, policy and what the model sees; aggregates context across serversDesktop chat apps, coding CLIs, IDEs, a framework-built agent
ClientConnector inside the host, one per serverSpeaks the protocol to exactly one server: transport, version/capability metadata, request IDs, routing results backADK McpToolset connection; an IDE's per-server connection
ServerProgram exposing capabilities over MCPAdvertises and executes tools, serves resources, provides prompts; validates input; enforces access controlFilesystem, Git hosting, Postgres, ticketing SaaS, managed cloud data

Key relationships

Rule of thumb

"Who decides whether this tool call runs?" → the host (often via a human-in-the-loop prompt), not the server and not the model.

Common trap

"The client is the end-user app." In MCP vocabulary the host is the app; the client is the per-server connector inside it.

02

2. Primitives: Tools, Resources, Prompts

Servers expose three primitives. The key distinction is who controls each one.

PrimitiveControl modelPurposeDiscovery / useTypical UI
ToolsModel-controlled (host-gated)Executable actions with side effects or computationtools/list → tools/callModel decides; host may ask the user to approve
ResourcesApplication-controlledContext/data identified by URI (files, schemas, records, catalogs)resources/list, resources/templates/list → resources/readApp attaches them (file picker, @-mention, auto-include)
PromptsUser-controlledReusable, parameterized message templates and workflowsprompts/list → prompts/getSlash commands, menu items
You want to…UseWhy
Let the model take an action or run a parameterized queryToolModel decides when; side effects gated by the host
Give the model read-only context chosen by the app or userResourceURI-addressable, no side effects, app decides when to attach
Show the agent what content exists before it exploresResource catalogOne read replaces many exploratory list/search tool calls
Offer a tested, repeatable workflow a human triggersPromptServer-owned template, invoked explicitly (e.g. /review-pr 42)

Tools

  • Fields: name, description, inputSchema (JSON Schema); optional title, outputSchema, annotations, icons.
  • Results: content (text, image, audio, resource links) and/or structuredContent (matches outputSchema), plus isError.
  • Annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) are hints only. Never base tool-use decisions on annotations from untrusted servers.

Prompts

Move prompt engineering into the server: the domain owner ships a tested template (e.g. /review-pr {pr_number}) and fills context server-side. prompts/get returns messages ready to inject.

Client-side features (2026-07-28): Elicitation is active (§6). Roots and Sampling are deprecated.

Resources as content catalogs (cut exploratory tool calls)

Without a catalog, an agent burns several turns calling list_*/search_* tools just to learn what exists. Expose a compact index as a resource and the agent gets the map up front, then calls the one tool it needs with a precise argument.

Catalog resourceReplaces
docs://index (doc hierarchy with titles and URIs)Repeated search_docs calls to discover page names
db://warehouse/schema (tables, columns, descriptions)Trial-and-error run_query calls against unknown tables
tickets://open/summary (open issues, one-line summaries)Paginating list_tickets to find a relevant issue

Mechanics: resources/list (paginated), resource templates (RFC 6570, e.g. tickets://{project}/{id}), resources/read (text or base64 binary + MIME type), listChanged and subscribe capabilities, and annotations (audience, priority 0–1, lastModified).

Rule of thumb

Tools for actions, resources for context, prompts for human-triggered workflows.

Common trap: "Resources are static data"

They're addressable data: they can be templated, generated on read and subscribable. What makes them resources is that the application decides when to include them, not the model.

03

3. Discovery & Tool Invocation

01

Discover

Servers MUST implement server/discover: supported versions, capabilities, identity, optional instructions.

02

List

tools/list (+ resources, prompts) is paginated (cursor → nextCursor) with caching hints (ttlMs, cacheScope).

03

Select & expose

The host filters, namespaces and converts tools into the model's function-calling format.

04

Invoke & return

Model emits a call → host (optionally after approval) sends tools/call → result is complete or input_required.

Per-request metadata, no handshake (2026-07-28): every request carries _meta with io.modelcontextprotocol/protocolVersion, client capabilities and client info; results carry server info. A version mismatch returns UnsupportedProtocolVersionError. Servers that declare tools.listChanged notify clients when the tool set changes.

JSON-RPC — tools/call request and result (trimmed)
{ "jsonrpc": "2.0", "id": 42, "method": "tools/call",
  "params": { "name": "get_order_status",
              "arguments": { "order_id": "A-1001" },
              "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28" } } }

{ "jsonrpc": "2.0", "id": 42,
  "result": { "resultType": "complete",
              "content": [{ "type": "text", "text": "Order A-1001 shipped on 2026-09-30." }],
              "structuredContent": { "order_id": "A-1001", "status": "SHIPPED" },
              "isError": false } }

Pre-2026 lifecycle (still in many SDKs)

From 2024-11-05 through 2025-11-25 a connection started with a three-step handshake: client initialize → server capabilities response → client notifications/initialized. The session stayed stateful until shutdown. The 2026-07-28 revision removed it.

Common trap: "Dynamic discovery means schemas cost no tokens"

Whatever the host exposes still enters the model's context. Savings come from host-side choices: filtering, lazy loading / tool search and prompt caching, not from the protocol itself.

04

4. Tool Descriptions & Interface Design

Descriptions are the main selection signal

The model picks a tool almost entirely from its name, description and input schema; it never sees your implementation. A one-line description ("Gets data") gives it nothing to separate two similar tools, so selection becomes a coin flip.

A good description states…Example
What it does and returns"Returns current stock level, reserved quantity and warehouse for one SKU."
When to use it"Use when the user asks whether an item is available or how many are left."
When NOT to use it (boundary)"For historical sales volumes, use get_sales_history instead."
Input formats"sku: 8-character code like AB-12345. Not the product name."
Example queries"'Do we still have the blue kettle?' → resolve SKU first, then call this."
Edge cases"Returns quantity: 0 with isError: false when the SKU exists but is out of stock."
SymptomFix
Two tools with near-identical descriptions get confusedRewrite both: purpose, inputs, outputs and "use X instead when…" boundaries
A generic tool does several unrelated jobs (process_document)Split into purpose-specific tools (extract_invoice_fields, summarize_contract, check_signature_present)
Names don't convey intent (tool_a, query2)Rename with verb + object; namespace by domain (inventory_get_stock)
Several tools overlap heavilyConsolidate into one tool with a constrained parameter (report_type: "daily" | "weekly")
Agent prefers a built-in generic tool (shell search, file read) over a more capable MCP toolExpand the MCP tool's description: what it can do that the generic tool can't, and what it returns
A system-prompt rule ("always search first") overrides good descriptionsReview the system prompt for keyword-sensitive instructions and align them with the tool descriptions

Spec naming guidance: tool names SHOULD be 1–128 characters, case-sensitive, using only A–Z a–z 0–9 _ - ., and unique within a server. When a host aggregates servers and two expose search, the host SHOULD disambiguate (e.g. prefix a server identifier).

Interface rule (agent-first, not API-first)Why
Don't mirror REST 1:1. Design task-level tools (find_customer_orders)Fewer calls, less reasoning burden, fewer errors
Typed, constrained inputs (enums, formats, required fields)Fewer malformed calls; server still validates
Paginate and filter server-sideUnbounded results blow the context; some hosts cap or offload large outputs, silently losing detail
Truncate intelligently: summary + how to refineKeeps the model on track
Enrich results (semantic layer): resolve IDs to names, return structuredContentModel reasons over meaning, not raw keys
Structured, actionable errors (§5)Lets the model pick the right recovery
Annotate read-only vs. destructive; make writes idempotentEnables safe auto-approval and retries
Small, role-scoped toolset per agentSelection accuracy drops as the toolset grows
Version tool contracts; description changes = prompt changesAvoids silent behavior shifts
Python — FastMCP server with a well-described tool and a catalog resource
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("inventory")

@mcp.tool()
def inventory_get_stock(sku: str) -> dict:
    """Return current stock level, reserved quantity and warehouse for ONE SKU.
    Use when the user asks if an item is available. For past sales, use
    inventory_get_sales_history instead. sku format: 'AB-12345' (not a name)."""
    return {"sku": sku, "quantity": 12, "reserved": 3, "warehouse": "LYS-1"}

@mcp.resource("inventory://catalog")
def catalog() -> str:
    """Compact index of product families and SKU ranges (content catalog)."""
    return '{"kettles": "AB-10000..AB-19999", "toasters": "AB-20000..AB-29999"}'

SDK options

  • Official SDKs (Python, TypeScript, Java, C#, Go, Kotlin…): high-level decorators (FastMCP @mcp.tool, @mcp.resource, @mcp.prompt) generate JSON Schemas from type hints and docstrings; low-level handlers for full control.
  • Agent frameworks consume servers as toolsets (ADK McpToolset, LangChain MultiServerMCPClient) and some can expose their tools as servers.
  • Managed: vendor-hosted remote servers.

Common trap: schema generation

Output schemas must resolve to a JSON object. Untyped *args/**kwargs or non-serializable return types break schema generation at startup.

05

5. Structured Error Handling

Channel (spec)SignalMeaningWho reacts
Protocol errorJSON-RPC error object with a numeric code and a message (unknown tool, malformed request, unsupported method, server fault)The request could not be processed as a valid MCP callHost/client code; the model usually never sees it
Tool execution errorCallToolResult with isError: true and an explanation in contentTool ran but failed (upstream API failure, invalid argument values, business rule)The model, which reads the message and adjusts

The spec says errors originating inside the tool SHOULD be returned in the result with isError: true, not as a protocol error, otherwise the model can't see what went wrong. Clients SHOULD pass tool execution errors to the model. Recent revisions also classify input validation failures (for example a date in the past) as tool execution errors, so the model gets a chance to fix its arguments.

JSON-RPC 2.0 error codes

MCP reuses the standard JSON-RPC 2.0 codes. Don't invent extra fields to carry meaning: use the code, a clear message and, if needed, the optional data member.

CodeNameTypical MCP trigger
-32700Parse errorThe body is not valid JSON
-32600Invalid requestValid JSON, but not a valid JSON-RPC request (e.g. missing jsonrpc or method)
-32601Method not foundThe server does not implement the method, e.g. a capability it never advertised
-32602Invalid paramsWrong parameter structure, or tools/call names a tool that does not exist
-32603Internal errorUnexpected failure inside the server while handling the request
-32000 … -32099Server errorReserved for implementation-defined server errors; document any code you use
JSON — protocol error (unknown tool)
{ "jsonrpc": "2.0", "id": 3,
  "error": {
    "code": -32602,
    "message": "Unknown tool: ship_parcel_v1" } }
JSON — tool execution error (model can fix it)
{ "jsonrpc": "2.0", "id": 7,
  "result": { "resultType": "complete",
    "isError": true,
    "content": [{ "type": "text",
      "text": "Pickup date 2026-10-02 is in the past.
        Use a date on or after 2026-11-02 (YYYY-MM-DD)." }] } }

Error text the model can act on

  • What failed? Name the argument, record or upstream system.
  • Why? The rule or condition that was violated.
  • What would succeed? The valid format, range or next step.
  • Is repeating the call pointless? Say so when only the user or an administrator can unblock it.

If the tool declares an outputSchema, structuredContent must conform to it; keep error explanations in content rather than bending the schema.

Failure vs. a valid empty answer

  • Lookup ran, matched nothing: a normal result (isError absent or false) with an empty list, e.g. "0 shipments for customer C-88".
  • Backend unreachable or refused access: isError: true with a message saying the lookup could not be performed.

Retries are host policy, informed by annotations

The protocol does not define retry semantics. Retry decisions live in the host or agent framework.

SignalHow it informs retries
idempotentHint: trueRepeating the call with the same arguments has no extra effect, so an automatic retry after a timeout is low risk
idempotentHint: false / destructiveHint: trueDon't auto-retry blindly. Prefer a server-supported idempotency key argument, or confirm with the user first
readOnlyHint: trueNo side effects; retries only cost latency and quota

Annotations are hints: treat them as untrusted unless the server itself is trusted. Bound every retry loop (attempts, backoff, overall timeout).

Common trap: one generic error string for every failure

The model can't tell a typo it could fix from a condition only a human can resolve, so it loops on hopeless calls or gives up on recoverable ones.

Common trap: except: return []

Swallowing an exception into an empty list makes the model state "no records exist" with confidence when the truth is "I couldn't check".

06

6. Elicitation & Multi-Round-Trip Input

01

Call

Client sends tools/call.

02

Input required

Server returns resultType: "input_required" with inputRequests (e.g. elicitation/create) and an opaque requestState.

03

Ask the human

Host renders the request and collects the answer (accept, decline or cancel).

04

Retry

Client retries with inputResponses + the same requestState, using a new JSON-RPC id. The server stays stateless.

ModeWhat happensUse for
FormServer sends a requestedSchema; host renders a form. Data is exposed to the clientMissing parameters, confirmations, choices
URLUser completes the flow out-of-band in a browser. Data (other than the URL) is not exposed to the clientThird-party OAuth, payments, entering API keys

Common trap: credentials via form elicitation

Sensitive credentials should use URL mode so they never transit the MCP client. The server stores the resulting third-party tokens itself and must not reuse the client's MCP access token upstream.

07

7. Transports

TransportHow it worksWhenStatus
stdioHost spawns the server as a subprocess; newline-delimited JSON-RPC on stdin/stdout; stderr is for logs onlyLocal tools, dev machines, IDE pluginsCurrent
Streamable HTTPSingle endpoint (e.g. /mcp); each message is an HTTP POST; server replies with JSON or an SSE streamRemote, shared, multi-tenant serversCurrent (standard for remote)
HTTP+SSE (two endpoints)Separate SSE GET stream plus POST endpointLegacy clients onlyDeprecated since 2025-03-26
Streamable HTTP aspect2025-03-26 → 2025-11-252026-07-28
SessionMcp-Session-Id assigned at initializeNo sessions; each request is self-describing via _meta
HeadersMcp-Session-Id, MCP-Protocol-VersionMCP-Protocol-Version, Mcp-Method, Mcp-Name so gateways route and authorize without parsing the body
Server → client notificationsGET SSE streamsubscriptions/listen long-lived stream

Rule of thumb

stdio = "one user, one machine, the host owns the process." Streamable HTTP = "a network service." The stateless 2026 design lets remote servers scale horizontally behind ordinary load balancers, no sticky sessions.

Common trap: "SSE is the remote transport"

Standalone HTTP+SSE is deprecated. Streamable HTTP may use SSE to stream a response, but it's a different transport.

08

8. Server Deployment & Configuration Scoping

DimensionLocal (stdio subprocess)Remote (Streamable HTTP)
Where it runsUser's machine, spawned by the hostCloud/service endpoint
Trust boundaryRuns with the user's OS privilegesNetwork service: treat as an external API
CredentialsEnvironment variables / local config; stdio should not use the HTTP OAuth flowOAuth 2.1 bearer tokens (or API keys/IAM in managed offerings)
Tenancy & updatesSingle user; user updates the packageMulti-tenant; provider updates centrally
Main risksSupply chain, local file/command accessToken misuse, confused deputy, SSRF, tenant leakage
MitigationsPin versions, containerize, non-root, restrict pathsAudience validation, narrow scopes, gateway policy, audit
Config scopeTypical locationShared?Use for
ProjectFile committed at the repo root (e.g. .mcp.json)Yes, via version controlTeam tooling everyone on the project needs (issue tracker, internal docs, staging DB)
UserFile in the user's home directoryNoPersonal or experimental servers used across projects
Local / private per-projectUser config keyed by project pathNoPersonal overrides for one project

Example host: Claude Code

Project scope = .mcp.json at the project root; user and local scopes live in ~/.claude.json; on name clashes local > project > user; project-scoped servers trigger an approval prompt the first time, so a cloned repo can't launch processes without consent. Servers from every scope are merged: the host connects to all configured servers and their tools are offered to the model together in the same session (Claude Code defers full schemas behind tool search by default; alwaysLoad: true preloads one server's tools). Full details in the Claude Code guide § MCP and the Claude Agent SDK guide.

JSON — project-scoped config, committed (secrets via environment-variable expansion)
{
  "mcpServers": {
    "tracker": {
      "type": "http",
      "url": "${TRACKER_URL:-https://tracker.example.com}/mcp",
      "headers": { "Authorization": "Bearer ${TRACKER_TOKEN}" }
    },
    "inventory": {
      "type": "stdio",
      "command": "uvx",
      "args": ["inventory-mcp-server==1.4.2"],
      "env": { "INVENTORY_API_KEY": "${INVENTORY_API_KEY}" }
    }
  }
}

Secrets: environment-variable expansion

  • A committed project config must never contain secrets. Reference them: ${VAR} expands from each developer's environment; ${VAR:-default} adds a fallback (Claude Code supports both in command, args, env, url, headers).
  • Exact keys vary by host (url, httpUrl, type: "http"). The principles don't: pin versions, inject secrets from the environment or a secret manager.

Community vs. custom servers

  • Existing community/vendor server for standard integrations (issue trackers, Git hosting, chat, common DBs). Still vet it: review tool definitions, pin the version, prefer official or registry-listed servers.
  • Custom server for team-specific workflows, internal systems, or when you need tighter tool design, filtering or policy.

Common traps

(1) Hard-coding a token in a committed config "because the repo is private". (2) Putting a team-wide server in one person's user config, so nobody else gets it. (3) Building a custom server for a mainstream SaaS integration "for control" instead of starting with a vetted existing one.

09

9. Authentication & Authorization

LayerQuestionMechanism
AuthenticationWho is the client/user?OAuth 2.1 token (remote); OS user + env credentials (local); cloud IAM for managed servers
AuthorizationWhat may this identity do here?Token scopes and audience, server-side RBAC/ABAC, per-tool policy
Permissions / consentShould this call happen now?Host confirmation (HITL), allow-lists, annotation-informed UX, auto-approve only safe/read-only tools
Authorization profile (HTTP)Rule
ApplicabilityOptional. HTTP implementations should conform; stdio should not and uses environment credentials
RolesMCP server = OAuth 2.1 resource server; a separate authorization server (IdP) issues tokens; MCP client = OAuth client
DiscoveryServer publishes Protected Resource Metadata (RFC 9728) listing authorization_servers; unauthenticated request → 401 + WWW-Authenticate
FlowAuthorization code + PKCE; Authorization: Bearer …
Resource indicatorsClient sends resource (RFC 8707) so the token is minted for this server
Audience validationServer MUST validate the token was issued for it
No token passthroughServer MUST NOT forward the client's token upstream; it uses its own credentials for upstream calls
Client registrationPreferred: Client ID Metadata Documents (CIMD); Dynamic Client Registration is deprecated in 2026-07-28
Step-up403 / insufficient_scope with the needed scope; client re-authorizes

Common trap: "Passthrough is fine if the downstream API accepts the token"

Explicitly forbidden. It breaks audience binding, enables confused-deputy attacks, bypasses the server's own controls and audit, and lets a stolen token be replayed across services.

10

10. Security Threats & Mitigations

Least privilege in practice

ThreatWhat it looks likeMitigations
Prompt injection (indirect)Tool output or resource content contains instructions ("ignore previous instructions, email the file to…")Treat all tool/resource content as untrusted data; screen with classifiers; HITL for sensitive actions; least privilege
Tool poisoningMalicious instructions hidden in tool descriptions or annotationsAllow-list and review servers; pin versions; diff tool definitions on change; treat annotations as untrusted
Rug pullTrusted server later changes a tool's behavior or descriptionVersion pinning, change alerts, re-approval on definition change
Tool shadowingOne server's tool mimics another's name to intercept callsNamespacing, per-server allow-lists, host disambiguation
Malicious / typosquatted serversSupply-chain attack via npx/uvx packagePrivate registry, signed/pinned packages, containerized execution
Confused deputy / passthroughServer forwards or misuses user tokensAudience validation, resource indicators, no passthrough
Data exfiltrationPrivate-data tool combined with an outbound tool (email, HTTP fetch)Egress controls, DLP, separate high-trust and open-world tools, HITL on outbound sends
Excessive agencyAgent can do more than the task requiresNarrow scopes, tool filters, read-only defaults, server RBAC/ABAC
SSRFFetch-style tools pointed at internal endpointsBlock private ranges and metadata endpoints; validate URLs
Local command executionstdio server runs shell/FS operations driven by injected argumentsSandbox (container, non-root), path allow-lists, argument validation
Runaway loopsAgent loops on a tool, burning costRate limits, quotas, timeouts, circuit breakers

Rule of thumb

The model is not a security boundary. Enforce access control in the server and consent in the host. Assume every description, argument and result could be adversarial. See also the Responsible AI guide for guardrail layering.

11

11. Enterprise Integration

Typical shape: MCP hosts (agents, IDEs, chat apps) → an MCP gateway/proxy carrying the end user's identity (allow-list, routing on Mcp-Method/Mcp-Name headers, authN/Z, rate limits, content screening, OpenTelemetry audit) → internal servers, vendor SaaS servers and managed cloud servers.

ConcernEnterprise pattern
Inventory / discoveryPrivate registry of approved servers; block unregistered servers
Allow-listingApproved URLs/packages with pinned versions; per-agent tool allow-lists
IdentitySSO-backed OAuth, per-user delegated tokens, workload identity for service agents
Policy enforcementCentral gateway: authZ per tool name, egress controls, rate limits, circuit breakers
Content safetyPrompt-injection / sensitive-data screening of tool inputs and outputs; DLP
ObservabilityLog every tools/call (user, agent, tool, redacted args, latency, outcome); OpenTelemetry traces (replaces deprecated protocol logging)
IsolationContainerized servers, network policies, tenant separation, sandboxed untrusted connectors
LifecycleVersioned servers, schema change review (descriptions are effectively prompts), staged rollout
Python — open-source framework example (ADK): consume a remote server with least privilege
from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool import McpToolset, StreamableHTTPConnectionParams

inventory = McpToolset(
    connection_params=StreamableHTTPConnectionParams(url="https://inventory.internal/mcp"),
    tool_filter=["inventory_get_stock", "inventory_list_warehouses"],  # read-only subset
)

agent = LlmAgent(model="gemini-2.5-flash", name="stock_agent",
                 instruction="Answer stock questions using the tools.",
                 tools=[inventory])

Managed servers

Cloud providers also offer managed remote MCP servers for their own services (IAM-controlled, with optional content screening). See the vendor guides on the hub and the ADK guide.

Rule of thumb

An enterprise answer mentions four things: registry/allow-list, per-user OAuth with audience-bound tokens, a gateway for policy and audit, and host-side HITL for destructive tools.

12

12. MCP vs. API, Function Calling, Frameworks & A2A

REST/gRPC APIMCP server
AudienceDeterministic software written by developersAI applications and models
DiscoveryDocs / OpenAPI read by humans at build timeRuntime, self-describing with descriptions aimed at the model
ShapeMany fine-grained endpointsTask-oriented tools; context as resources; templates as prompts
Relationship—MCP usually wraps existing APIs; it doesn't replace them
Function (tool) callingMCP
What it isA model API feature: the model returns "call X with args Y"A protocol between host and external servers for discovering and executing tools (plus resources/prompts)
Where it livesBetween your code and the LLMBetween your host and tool providers
Who executesYour application codeThe MCP server (host sends tools/call)
PortabilityDeclaration format differs per providerSame server works with any host and model

MCP vs. agent framework

Frameworks (ADK, LangGraph, Claude Agent SDK…) own reasoning and orchestration: agent loop, planning, which tool and when, state, memory, evaluation. MCP owns connectivity. Frameworks act as MCP hosts/clients and can expose tools as MCP servers. See Agentic Design Patterns.

MCP vs. A2A

MCP = agent-to-tool (vertical; capability provider without its own goals; tools/list; tool call). A2A = agent-to-agent (horizontal; opaque autonomous agent; Agent Card; Task lifecycle). Real systems use both. See the A2A guide.

Common trap: "MCP replaces function calling"

The model still uses function calling to choose a tool. MCP standardizes where tool definitions come from and how the call is executed. They're complementary.

Interoperability protocol, not orchestration framework

MCP defines messages, methods, transports, capability metadata and an auth profile. It does not define planning, tool ordering/retries, memory, multi-agent routing or guardrail policy. That narrow contract is what enables the M + N ecosystem.

13

13. Spec Evolution & Deprecations

RevisionHeadline changes
2024-11-05Initial release: stdio + HTTP+SSE; tools, resources, prompts, sampling, roots, logging
2025-03-26Streamable HTTP replaces HTTP+SSE; first OAuth 2.1 framework; tool annotations
2025-06-18Server = OAuth resource server with PRM (RFC 9728); RFC 8707; elicitation; structured tool output; MCP-Protocol-Version header
2025-11-25Tasks (long-running), server identity, official extensions, URL-mode elicitation, CIMD
2026-07-28Stateless: no initialize, per-request _meta, server/discover, input_required round-trips, session-free Streamable HTTP (Mcp-Method/Mcp-Name, subscriptions/listen)
Deprecated in 2026-07-28 (removable on/after 2027-07-28)Replacement
RootsPass paths via tool parameters, resource URIs or server config
Sampling (server borrows the host's LLM)Server calls an LLM provider API directly
Protocol loggingstderr (stdio) or OpenTelemetry
Dynamic Client RegistrationClient ID Metadata Documents
HTTP+SSE transport (deprecated since 2025-03-26)Streamable HTTP

Common trap: answering a "current spec" question with old mechanics

The initialize → initialized handshake and Mcp-Session-Id are correct for 2025-11-25 and earlier but were removed in 2026-07-28. If a question names a revision, answer for that revision.

14

14. Quick Review, Glossary & Practice Quiz

If the question says…Think…
"Who approves a destructive tool call?"Host (HITL), not the server or model
"Model decides" / "App attaches context" / "User picks a slash command"Tool / Resource / Prompt
"Agent wastes turns listing things to find what exists"Expose a resource catalog
"Agent keeps picking the wrong one of two similar tools"Improve descriptions; rename/split overlapping tools
"Tool failed; how does the model find out?"isError: true in the result, not a JSON-RPC error
"Should the agent retry?"Host policy: retry idempotent/read-only tools with bounded backoff; confirm before repeating destructive calls
"tools/call names a tool that doesn't exist"Protocol error, JSON-RPC -32602
"Query matched nothing"Valid empty result, isError: false
"Team needs the same server; secrets stay out of git"Project-scoped config + ${VAR} expansion
"Personal experimental server"User-scoped config
"Standard SaaS integration"Vetted community/vendor server first; custom for team-specific workflows
"Local, user's machine" / "Remote, multi-tenant"stdio + env credentials / Streamable HTTP + OAuth 2.1
"Server forwards user's token upstream"Token passthrough: forbidden
"Malicious instructions in a tool description"Tool poisoning: allow-list, pin, diff, treat annotations as untrusted
"Third-party OAuth / API key during a tool call"URL-mode elicitation
Annotations (tool)
readOnlyHint, destructiveHint, idempotentHint, openWorldHint. Hints only; untrusted unless the server is trusted.
CIMD
Client ID Metadata Document: OAuth client_id is an HTTPS URL to the client's metadata; replaces Dynamic Client Registration.
Confused deputy
A privileged component tricked into acting for an unauthorized party.
Elicitation
Server-initiated request for user input, in form mode (in-band) or URL mode (out-of-band).
Host / Client / Server
The AI app that owns consent / the per-server connector inside it / the program exposing tools, resources and prompts.
input_required
2026-07-28 result type asking the client to gather input and retry with requestState.
isError / JSON-RPC error code
Result flag marking a tool execution error the model should see / numeric code of a protocol error (-32700, -32600, -32601, -32602, -32603, or -32000…-32099 for implementation-defined server errors).
Project vs. user scope
Server config committed with the repo and shared by the team (e.g. .mcp.json) vs. personal config across all of a user's projects.
PRM
Protected Resource Metadata (RFC 9728): tells clients which authorization servers protect the MCP server.
Resource catalog
A resource listing what content exists (docs index, schema, issue summary) so the agent skips exploratory tool calls.
server/discover
Required 2026-07-28 method returning supported versions, capabilities and server identity.
Token passthrough
Forbidden anti-pattern of forwarding a client's token to downstream APIs.
Tool poisoning
Hiding malicious instructions in tool descriptions or annotations.

Related guides

Practice

Drill scenario questions on primitives and resource catalogs, tool description design, structured errors and retryability, transports, project vs. user scoping, OAuth and the MCP threat model.

Back to the Hub Take the practice quiz →

Self-Assessment Questions (Click to Reveal)

Q1. What are the three server primitives and who controls each?

Tools (model-controlled, host-gated), resources (application-controlled), prompts (user-controlled).

Q2. Why expose a documentation index as a resource instead of only a search tool?

The agent sees what exists up front and calls the one tool it needs, instead of burning turns on exploratory searches.

Q3. Two tools are often confused. What do you change first?

Their descriptions: purpose, input formats, outputs, examples and "use X instead when…" boundaries. Then rename or split tools that genuinely overlap.

Q4. A tool's backend times out. How should the result look?

A CallToolResult with isError: true and a message saying the upstream system timed out. Whether to retry is host policy, safest for tools annotated idempotentHint: true.

Q5. How do you distinguish "no matches" from "couldn't check"?

No matches = normal result with an empty list. Couldn't check = isError: true with a message explaining the lookup could not be performed.

Q6. Where do shared team servers and personal servers go?

Shared: project-scoped config committed to the repo, with secrets via ${VAR}. Personal/experimental: user-scoped config.

Q7. A remote server receives a token minted for another API. What should it do?

Reject it: the audience must match, the client must use an RFC 8707 resource indicator, and the server must never pass tokens through.

Q8. What changed in the 2026-07-28 revision?

Stateless protocol: no initialize handshake, per-request _meta, server/discover, session-free Streamable HTTP and input_required round-trips. Roots, sampling, protocol logging and DCR are deprecated.