The Model Context Protocol (MCP) is an open protocol that lets an AI application connect to outside tools and data through one standard interface. A host application runs MCP clients, each client talks to one MCP server, and they exchange JSON-RPC 2.0 messages over stdio or Streamable HTTP, with OAuth 2.1 based authorization for remote servers.
The current revision on modelcontextprotocol.io, checked on 23 October 2026, is 2026-07-28, and it breaks with everything before it: the initialize handshake and protocol sessions are gone, and every request carries its own version and capabilities. This guide covers the whole protocol as it stands today, what older revisions did differently, and the security properties the specification documents.
Key takeaways
- MCP has three roles. A host is the AI application, a client is a connector inside the host, and a server offers context and capabilities. Every message is JSON-RPC 2.0.
- Five dated revisions exist: 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25 and 2026-07-28. The spec calls 2025-11-25 and earlier "legacy" (handshake based) and 2026-07-28 "modern" (stateless).
- Servers offer three primitives: tools (model controlled functions), resources (application driven data addressed by URI) and prompts (user controlled templates). Clients can offer elicitation, sampling and roots, though 2026-07-28 deprecates sampling, roots and logging.
- Two standard transports exist: stdio for a local subprocess and Streamable HTTP for remote servers. The original HTTP+SSE transport has been deprecated since 2025-03-26.
- Authorization applies to HTTP transports only. The MCP server is an OAuth 2.1 resource server, publishes RFC 9728 Protected Resource Metadata, and clients must send RFC 8707 resource indicators so tokens are bound to one server.
- The spec's security best practices page names the main weakness classes: confused deputy, token passthrough, SSRF during discovery, state handle hijacking and local server compromise. Tool descriptions are untrusted input.
- In Claude Code,
claude mcp listshows every configured server,/mcpmanages sign in, andoauth.scopespins the scopes a server gets.
What MCP is for, where it sits and which document defines it
The problem it solves
Without a shared protocol, every AI application needs its own integration for every database, ticket system or code host. The specification says MCP "takes some inspiration from the Language Server Protocol", which solved the same problem for editors: write the integration once as a server and every compliant client can use it. MCP standardises sharing context with language models, exposing tools to AI systems, and composing integrations into workflows.
Hosts, clients and servers
The roles are defined in the specification overview:
| Role | What it is | Example in practice |
|---|---|---|
| Host | The LLM application that initiates connections | A desktop chat app, an IDE assistant, a coding agent in a terminal |
| Client | A connector inside the host, one per server connection | The component in Claude Code that talks to one configured server |
| Server | A service that provides context and capabilities | A local process exposing file search, or a remote endpoint exposing a ticketing API |
The host owns the user relationship: it decides which servers are connected, shows consent prompts and passes tool results to the model. The server never talks to the model directly, which is why the specification puts most consent duties on hosts.
Where it sits in the stack
MCP is an application layer protocol with JSON-RPC 2.0 messages. Locally it runs over a child process's standard input and output; remotely it runs over HTTP, usually HTTPS. It has no port of its own: a remote server lives at whatever URL its operator picks (the spec's example is https://example.com/mcp), and a stdio server has no port at all.
The defining standard
MCP is not an IETF or ISO standard. The normative document is the specification at modelcontextprotocol.io, named by revision date and "based on the TypeScript schema in schema.ts", with BCP 14 keywords such as MUST and SHOULD. It builds on these published standards:
| Standard | What MCP uses it for |
|---|---|
| JSON-RPC 2.0 (jsonrpc.org, updated 2013-01-04) | Message format for requests, responses, notifications and errors |
| JSON Schema 2020-12 | Default dialect for tool input and output schemas |
| OAuth 2.1 (draft-ietf-oauth-v2-1-13) | Authorization framework for HTTP transports |
| RFC 9728, April 2025 | OAuth 2.0 Protected Resource Metadata, how a server names its authorization server |
| RFC 8707, February 2020 | Resource Indicators, binding a token to one MCP server |
| RFC 8414 and OpenID Connect Discovery 1.0 | Authorization server metadata discovery |
| RFC 9207 | Issuer identification in authorization responses |
| RFC 7591 | Dynamic Client Registration, deprecated in 2026-07-28 |
| draft-ietf-oauth-client-id-metadata-document-00 | Client ID Metadata Documents, the preferred registration method |
Each revision supersedes the last. Revision 2026-07-28 also adopted a feature lifecycle with Active, Deprecated and Removed states and "a minimum twelve-month deprecation window".
How MCP works on the wire
JSON-RPC 2.0, the base layer
In JSON-RPC 2.0 a request has a method, optional params and an id; a response echoes the id with a result or an error; a notification has no id and gets no reply. MCP tightens those rules:
| Message type | JSON-RPC rule | MCP addition |
|---|---|---|
| Request | Has id, method, optional params | The id MUST be a string or integer and MUST NOT be null; it must not repeat an outstanding request id |
| Result response | Same id, a result member | result MUST include a resultType, either complete or input_required (2026-07-28) |
| Error response | Same id, an error with integer code and message | MCP partitions the server error range (see below) |
| Notification | No id, no reply | Receiver MUST NOT respond |
JSON-RPC reserves -32700 and -32600 to -32603 for standard errors and leaves -32000 to -32099 to implementations. MCP 2026-07-28 splits that range: -32000 to -32019 is legacy, and -32020 to -32099 belongs to the MCP specification, which defines three codes:
| Code | Name | When it is returned |
|---|---|---|
| -32020 | HeaderMismatch | An HTTP header does not match the same field in the JSON body |
| -32021 | MissingRequiredClientCapability | The request needs a capability the client did not declare |
| -32022 | UnsupportedProtocolVersion | The server does not implement the requested revision |
The old "resource not found" code -32002 became -32602 in 2026-07-28. JSON-RPC batching was added in 2025-03-26 and removed in 2025-06-18.
Per request metadata in the current revision
In 2026-07-28 there is no connection setup. Every client request carries reserved fields inside params._meta:
_meta key | Required | Meaning |
|---|---|---|
io.modelcontextprotocol/protocolVersion | Yes | The revision this request uses, for example 2026-07-28 |
io.modelcontextprotocol/clientCapabilities | Yes | The client capabilities relevant to this request |
io.modelcontextprotocol/clientInfo | No, but SHOULD be sent | Client name and version |
io.modelcontextprotocol/logLevel | No | Minimum log level for this request |
progressToken | No | Opts the request into progress notifications |
traceparent, tracestate, baggage | No | OpenTelemetry trace context |
A request missing a required field is rejected with -32602 (HTTP 400). Servers should name themselves in each result's _meta under io.modelcontextprotocol/serverInfo. Here is the specification's discovery exchange, which shows the shape every modern request follows:
{
"jsonrpc": "2.0",
"id": "discover-1",
"method": "server/discover",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "ExampleClient", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
The server replies:
{
"jsonrpc": "2.0",
"id": "discover-1",
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": { "tools": {}, "resources": {} },
"_meta": { "io.modelcontextprotocol/serverInfo": { "name": "ExampleServer", "version": "1.0.0" } },
"instructions": "This server provides weather and resource utilities.",
"ttlMs": 3600000,
"cacheScope": "public"
}
}
Initialisation and capability negotiation, then and now
Legacy revisions (2024-11-05 to 2025-11-25) open with a handshake. The client sends initialize with its preferred protocol version, capabilities and implementation details; the server replies with that version or another it supports, plus its own capabilities; the client sends notifications/initialized. Until then the client should send nothing but pings.
Revision 2026-07-28 removes the handshake. The server accepts or rejects each request on its own. An unsupported version gets UnsupportedProtocolVersion with a supported list, and the client retries. Servers MUST implement server/discover; clients MAY call it first. A server MUST NOT rely on a capability the client did not declare in that request, and returns MissingRequiredClientCapability if it needs one. Optional extensions such as Tasks (io.modelcontextprotocol/tasks) and MCP Apps are advertised in an extensions map.
Statelessness means "all the information needed to process a request is contained in the request itself." An open stdio process is not a conversation, and state that spans requests MUST be referenced by an explicit identifier passed on each call.
Interoperating across the two eras
A modern only client cannot talk to a 2025 server. The versioning page defines "dual era" implementations that speak both. On stdio, a dual era client probes with server/discover and falls back to initialize on any non modern error or a timeout; on HTTP it inspects the body of a 400 before falling back.
Server primitives: tools, resources and prompts
The three server features differ in who decides when they are used:
| Primitive | Controlled by | What it is | Core methods |
|---|---|---|---|
| Tools | The model | Functions the model can call, each with a name, description and JSON Schema inputSchema (and optional outputSchema) | tools/list, tools/call |
| Resources | The application | Data identified by a URI (RFC 3986), such as files or database schemas, that the host chooses to include as context | resources/list, resources/read, resources/templates/list |
| Prompts | The user | Server defined message templates the user picks, often shown as slash commands | prompts/list, prompts/get |
For prompts, user controlled "refers to who decides when the prompt is used, not who authors its content". Tools can carry annotations such as read only or destructive hints (added in 2025-03-26), but "clients MUST consider tool annotations to be untrusted unless they come from trusted servers." In 2026-07-28, list and read results carry ttlMs and cacheScope cache hints.
Client features: elicitation, sampling and roots
Clients can offer features to servers:
- Elicitation lets a server ask the user for input. Form mode collects structured data; URL mode sends the user to an external page for interactions that "must not pass through the MCP client". Form mode MUST NOT ask for passwords, API keys, tokens or payment credentials.
- Sampling lets a server request a completion from the client's model. Deprecated in 2026-07-28; integrate with a model provider directly instead.
- Roots tell a server which directories matter. They "are informational guidance rather than an access-control mechanism". Deprecated in 2026-07-28; pass paths as tool parameters or configuration.
Logging is deprecated too. The registry gives the earliest removal for all three as the first revision on or after 2027-07-28.
Multi round trip requests
Earlier revisions let a server send its own requests, such as elicitation/create, to the client mid tool call. Revision 2026-07-28 replaces that with Multi Round-Trip Requests (MRTR):
Client Server
| tools/call (id 1) |
|---------------------------------------->|
| | needs user input
| result: resultType "input_required" |
| inputRequests + requestState |
|<----------------------------------------|
| gathers input from the user |
| tools/call (id 2) |
| original params + inputResponses |
| + requestState echoed back |
|---------------------------------------->|
| result: resultType "complete" |
|<----------------------------------------|
Servers must treat requestState as "attacker-controlled input": if it affects authorization or business logic, protect it with an HMAC or AEAD, and bind it to the principal, a short expiry and the originating request.
Subscriptions, progress and cancellation
Change notifications now flow on one long lived subscriptions/listen request, carrying only the types the client asked for; it replaced resources/subscribe and the HTTP GET stream. On stdio, cancellation is a notifications/cancelled message; on Streamable HTTP, closing the response stream cancels. ping was removed in 2026-07-28.
Transports: stdio, Streamable HTTP and what happened to HTTP+SSE
stdio
The client launches the server as a subprocess. The server reads newline delimited JSON-RPC from stdin and writes it to stdout; nothing else may go to stdout, and logs go to stderr. In 2026-07-28 the server never writes requests to stdout. To shut down, the client closes stdin and escalates to SIGTERM then SIGKILL if needed. The same framing works over Unix sockets or TCP for custom transports.
Streamable HTTP
Introduced in 2025-03-26, Streamable HTTP uses one endpoint that accepts POST. In 2026-07-28 every client message is its own POST, accepting application/json and text/event-stream. An accepted notification gets 202 Accepted; a request gets a single JSON object or an SSE stream scoped to that request. Resumption via Last-Event-ID is gone, so a broken stream means re-sending the request with a new id. Selected fields are mirrored into headers so gateways can route without parsing JSON:
| HTTP header | Source | Required | Notes |
|---|---|---|---|
MCP-Protocol-Version | _meta protocol version | Every POST | Must match the body, or 400 with HeaderMismatch; introduced in 2025-06-18 |
Mcp-Method | JSON-RPC method | All requests | Added in 2026-07-28 |
Mcp-Name | params.name or params.uri | tools/call, resources/read, prompts/get | Base64 sentinel encoding if not plain ASCII |
Authorization | OAuth access token | Every request to a protected server | Bearer token, never in the query string |
Origin | Set by browsers | Validated on every connection | Invalid Origin must get 403 |
Custom headers via x-mcp-header | Annotated tool parameters | Clients MUST support it | Added in 2026-07-28 |
Status codes: 400 for malformed requests and version or header errors, 404 with a -32601 body for an unknown method, 401 for a bad token, 403 for a bad Origin. Revision 2026-07-28 also removed the GET stream and the Mcp-Session-Id session header; servers needing state mint their own handles.
What happened to HTTP+SSE
Revision 2024-11-05 defined an HTTP with SSE transport. Revision 2025-03-26 replaced it with Streamable HTTP, and 2026-07-28 formally lists it as Deprecated, with earliest removal "three months after SEP-2596 reaches Final". Some clients, Claude Code among them, still accept SSE for older servers. For choosing between the current transports, see stdio or Streamable HTTP.
Authorization: OAuth 2.1 for HTTP servers
Scope of the authorization spec
Authorization is optional. HTTP transports SHOULD follow the authorization specification; stdio SHOULD NOT and should "retrieve credentials from the environment" instead.
Roles
The MCP server is an OAuth 2.1 resource server, the MCP client is an OAuth 2.1 client, and an authorization server, hosted anywhere, signs the user in and issues tokens.
The flow, step by step
The 2026-07-28 flow, as a text diagram:
Client MCP server Authorization server Browser
| request, no token | | |
|--------------------------->| | |
| 401 + WWW-Authenticate | | |
| resource_metadata, scope | | |
|<---------------------------| | |
| GET protected resource | | |
| metadata (RFC 9728) | | |
|--------------------------->| | |
| authorization_servers list | | |
|<---------------------------| | |
| GET AS metadata (RFC 8414 or OIDC Discovery) | |
|------------------------------------------------------>| |
| register: CIMD URL, pre-registered id, or DCR | |
| PKCE + resource parameter + scope, record issuer | |
| open authorization URL --------------------------------------------------- > |
| | user signs in and consents <---------------|
| code + iss on redirect <--------------------------------------------------------|
| validate iss (RFC 9207) | | |
| token request + code_verifier + resource | |
|------------------------------------------------------>| |
| access token (+ refresh) | | |
|<------------------------------------------------------| |
| request + Bearer token | | |
|--------------------------->| | |
- Find the authorization server. MCP servers MUST publish RFC 9728 Protected Resource Metadata, located via the
resource_metadataparameter of the 401 challenge or a well known URI such as/.well-known/oauth-protected-resource. Clients then fetch RFC 8414 or OpenID Connect Discovery metadata and MUST support both. - Register. In priority order: a Client ID Metadata Document (an HTTPS URL as
client_id), pre-registration, or RFC 7591 Dynamic Client Registration, now deprecated. - Pick scopes. Use the
scopefrom the 401 challenge, elsescopes_supported, which should be the minimal set; request more later through step up authorization. - Authorize with PKCE, recording the expected issuer. Since 2026-07-28, clients MUST check a present
issagainst it (RFC 9207) before redeeming the code, which blocks mix-up attacks. - Call the server with
Authorization: Beareron every request, never in the query string.
Resource indicators and audience binding
Clients MUST send the RFC 8707 resource parameter in authorization and token requests, set to the server's canonical URI such as https://mcp.example.com/mcp. Servers MUST check each token was issued for them and MUST NOT accept or pass along any other. A token for one server therefore cannot be replayed at another or forwarded downstream. Refresh tokens must be kept confidential and are never guaranteed.
How authorization changed by revision
| Revision | Authorization state |
|---|---|
| 2024-11-05 | No authorization framework in the specification |
| 2025-03-26 | First authorization framework, based on OAuth 2.1 |
| 2025-06-18 | MCP servers classified as OAuth resource servers with RFC 9728 Protected Resource Metadata; clients must send RFC 8707 resource indicators; new security best practices page |
| 2025-11-25 | OpenID Connect Discovery support; incremental scope consent through WWW-Authenticate; Client ID Metadata Documents recommended; well known metadata fallback when the header is absent |
| 2026-07-28 | RFC 9207 iss validation; application_type required in dynamic registration; client credentials bound to the issuing authorization server; Dynamic Client Registration deprecated in favour of Client ID Metadata Documents |
See the 2025-06-18 resource server change for depth on that revision, and what is OAuth for the framework.
Versions and history
The full list of dated revisions on modelcontextprotocol.io, with the main changes each one's changelog records:
| Revision | Main changes | Status today |
|---|---|---|
| 2024-11-05 | First published revision: JSON-RPC base, initialize lifecycle, tools, resources, prompts, sampling, roots, stdio and HTTP+SSE transports | Legacy |
| 2025-03-26 | OAuth 2.1 authorization framework; Streamable HTTP replaces HTTP+SSE; JSON-RPC batching added; tool annotations; audio content; completions capability | Legacy |
| 2025-06-18 | Batching removed; structured tool output; servers as OAuth resource servers; RFC 8707 required; elicitation; resource links in tool results; MCP-Protocol-Version header; title fields | Legacy |
| 2025-11-25 | OIDC Discovery; icons; incremental scope consent; tool name guidance; URL mode elicitation; tool calling in sampling; Client ID Metadata Documents; experimental tasks; JSON Schema 2020-12 as default dialect; SSE polling | Legacy, and the most recent handshake based revision |
| 2026-07-28 | Stateless protocol: no initialize, no sessions, no Mcp-Session-Id, no GET stream, no resumability; server/discover; subscriptions/listen; MRTR and required resultType; tasks moved to an extension; Mcp-Method and Mcp-Name headers; cache hints; error code partition; iss validation; roots, sampling, logging and DCR deprecated | Current as of 23 October 2026 |
Many deployed servers will speak a legacy revision for some time, so dual era support matters. For the security impact of specific revisions, see the 2025-11-25 tasks abstraction and what 2026-07-28 changes for security.
Security properties and known weakness classes
What the protocol does and does not guarantee
| Property | What MCP provides | Where it comes from |
|---|---|---|
| Confidentiality in transit | None of its own | TLS on Streamable HTTP; a local pipe on stdio |
| Server authentication | None of its own | TLS certificate validation on HTTP |
| Client and user authorization | OAuth 2.1 on HTTP, optional | The authorization specification |
| Token audience binding | Yes, via RFC 8707 and server side audience checks | Required since 2025-06-18 |
| Message integrity | None beyond the transport | TLS; integrity protected requestState for MRTR |
| Replay protection | Limited; MRTR state should carry principal, expiry and request binding | MRTR rules in 2026-07-28 |
| Consent | Not enforceable by the protocol | The specification says MCP "cannot enforce these security principles at the protocol level" and puts the duty on implementers |
| Trust in tool descriptions | None | Annotations and descriptions are untrusted unless the server is trusted |
The specification says tools "represent arbitrary code execution and must be treated with appropriate caution", and hosts must get explicit consent before invoking a tool or sharing user data with a server.
Weakness classes from the specification's security best practices
Summarised as concepts from the 2026-07-28 Security Best Practices page. For a full threat model, see our MCP threat model for 2026 and MCP's expanding attack surface.
Confused deputy. A proxy server that uses one static client ID with a third party API can let a malicious client ride on a user's earlier consent at that third party and obtain a code the user never meant to grant. Proxies MUST collect per client consent first.
Token passthrough. A server accepts a token not issued to it and forwards it downstream, bypassing its own controls and audit trail. The authorization spec forbids it.
SSRF during discovery. OAuth discovery makes the client fetch URLs the server supplies. A malicious server can point them at internal or cloud metadata addresses, so server side clients must treat these fetches as SSRF risks.
State handle hijacking. With sessions gone, servers mint state handles. If a handle can be guessed and the server does not check its owner, someone else can use that state. Possession of a handle is never authentication; use random, expiring handles. In legacy revisions the same concern applied to Mcp-Session-Id values.
Tool description injection. The model reads tool names and descriptions as guidance, so a server can steer it through them. The Local Server Security guide advises reading tool lists before relying on a server, preferring clients that re-prompt when definitions change, and keeping the roster small.
Local server compromise and exposure. A local server runs with the user's privileges, so a malicious startup command or payload gets that access. One click setup MUST show the exact command. Local HTTP servers must validate Origin against DNS rebinding and should bind to 127.0.0.1.
Other classes on the page: authorization URLs with dangerous schemes (clients MUST allow only https, plus http on loopback), stdio proxies that turn a client side script injection into command execution, mix-up attacks (handled by iss checks), localhost redirect impersonation, and over broad scopes.
For how these map to approvals in practice, see the MCP permissions model.
Secure configuration and defender side checks
The commands below come from the official Claude Code MCP documentation at code.claude.com and the MCP Inspector documentation at modelcontextprotocol.io. Only run them against systems and servers you own or are authorised to test.
Server side settings that matter
- Choose modern only or dual era deliberately; a modern only server should list its versions in any error to
initialize. - Bind local HTTP servers to 127.0.0.1 and return 403 on an invalid
Origin. - Require authorization on remote servers: publish RFC 9728 metadata, challenge with a minimal
scope, and reject tokens not issued for your canonical URI. - Never forward client tokens downstream.
- Integrity protect
requestStateand use unguessable, owner checked state handles. - Validate tool inputs, rate limit calls and sanitise outputs, all server MUSTs on the tools page; never auto fetch remote
$refschemas.
Client and host settings that matter
- Show tool inputs and confirm sensitive operations, as the tools page recommends.
- Pin scopes, review tool descriptions before enabling a server, and remove servers you no longer use.
Check which servers Claude Code has configured
Claude Code stores MCP servers at three scopes. Local scope (the default) and user scope live in ~/.claude.json; project scope lives in .mcp.json at the project root and is meant to be checked into version control. List and inspect them from a terminal:
# List every configured server with a health status
claude mcp list
# Show the configuration and status of one server
claude mcp get <name>
# Remove a server; for a remote server this also deletes stored OAuth tokens
claude mcp remove <name>
Statuses include Connected, Needs authentication, Failed to connect, and "Pending approval" for project servers from .mcp.json you have not approved. It also warns when one server name has different endpoints in different scopes. Inside a session, /mcp lists every server, including connectors added in claude.ai (marked as such), and lets you authenticate, clear authentication or disable a server for the project.
Check and limit OAuth scopes
Set oauth.scopes in a server's configuration, which the documentation calls "the supported way to restrict an MCP server to a security-team-approved subset". Pinned scopes override discovered ones; if the server later returns insufficient_scope, the call fails naming the scope, and you decide whether to add it.
{
"mcpServers": {
"example": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"oauth": { "scopes": "tickets:read" }
}
}
}
Organisations can fix the server set with managed-mcp.json and allow and deny lists. Claude.ai connectors are managed at claude.ai/customize/connectors, where on Team and Enterprise plans only admins can add them.
Check what a server actually exposes
Read a server's tool list without a model in the loop using the MCP Inspector CLI:
# Local stdio server
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list
# Remote Streamable HTTP server
mcp-inspector --cli https://api.example.com/mcp --transport http --method tools/list
# Tool names only, as JSON
mcp-inspector --cli <server> --method tools/list --format json | jq '.result.tools[].name'
# Fail if any tool schema has error severity portability problems
mcp-inspector --cli <server> --method tools/list --strict > /dev/null
If a description holds instructions unrelated to the tool's job, remove the server. Saving the JSON and diffing it on each upgrade shows when definitions change.
The Claude API's MCP connector (beta header mcp-client-2025-11-20) is different: the server "must be publicly exposed through HTTP", and local stdio servers cannot be connected directly.
When to use MCP versus the alternatives
| Approach | Best for | Security properties | Operational cost |
|---|---|---|---|
| MCP | Exposing tools and data to many AI hosts through one integration | OAuth 2.1 with audience bound tokens on HTTP; host mediated consent; tool metadata untrusted | Moderate: run a server, track spec revisions, manage dual era support during the 2026 transition |
| Agent to agent protocol (A2A) | One autonomous agent delegating a task to another, possibly opaque, agent | Its official specification lists authentication, authorization, tracing and monitoring as design goals and uses HTTP, JSON-RPC 2.0, SSE and a gRPC binding | Moderate to high: agent discovery cards, long running task state, push notifications |
| Plain REST or GraphQL API | Deterministic integrations where code, not a model, decides what to call | Mature HTTP auth and gateway tooling; no model in the loop | Low if the API already exists; each AI host needs its own wrapper |
| Provider specific function calling | A single application calling a few of its own functions with one model provider | Your own code executes the function; no external server | Low for one app; repeated effort for each provider and app |
Rule of thumb: if a model chooses which capability to use and more than one AI host needs it, use MCP. If agents hand work to each other, use an agent to agent protocol, with each agent using MCP for its own tools. If the caller is ordinary code, a normal API is simpler. See MCP vs REST APIs for security tools and the A2A explainer.
Where Safeguard fits
Safeguard's MCP server at mcp.safeguard.sh is generally available and listed in the Claude connector directory; it uses OAuth 2.0 with PKCE and RFC 7591 dynamic client registration. Agent identity, which tracks which agent holds what access, is generally available.
MCP Guardrail, proxy URLs placed in front of MCP servers, is in early access, as is the AI Gateway, an LLM firewall with prompt injection, jailbreak and PII egress guardrails. Ask what each covers today before planning around it.
MCP SDKs and the HTTP and OAuth libraries beneath them are ordinary dependencies, covered by Safeguard's generally available SCA with transitive inventory, SPDX and CycloneDX SBOMs, and vulnerability management with EPSS, KEV and reachability. Safeguard does not inspect MCP traffic beyond the early access guardrail products.
Common mistakes
- Treating roots as a sandbox. The protocol does not enforce them; use OS or container isolation.
- Trusting tool annotations. A "read only" hint is only the server's claim.
- Forwarding the user's token downstream, which is forbidden token passthrough.
- Binding a local HTTP server to 0.0.0.0 without
Originchecks. - Using a state handle as proof of identity.
- Requesting every scope at sign in instead of stepping up.
- Approving a repository's
.mcp.jsonunread. It is a command your machine will run. - Assuming a 2026-07-28 client works with a 2025 server without dual era support.
- Keeping HTTP+SSE servers indefinitely; it has been deprecated since 2025-03-26.
FAQ
What is MCP in simple terms?
An open protocol that lets an AI application use outside tools and data through one standard interface. Clients inside the application each connect to one server and exchange JSON-RPC 2.0 messages.
What port does MCP use?
None by default. A stdio server is a local subprocess with no port; a Streamable HTTP server listens at whatever URL its operator chooses, typically over HTTPS.
Is MCP encrypted?
Not by itself. Remote traffic relies on TLS when the server uses HTTPS; stdio traffic stays in a local pipe.
What is the latest MCP specification version?
On 23 October 2026 it is 2026-07-28. Earlier revisions are 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.
Does MCP still use an initialize handshake?
Not in 2026-07-28, where every request carries its version and capabilities and servers implement server/discover. Revisions up to 2025-11-25 use initialize.
What is the difference between stdio and Streamable HTTP in MCP?
stdio runs the server as a child process over standard streams, with credentials from the environment. Streamable HTTP runs it as a service at one POST endpoint, answering with JSON or a per request SSE stream, with OAuth 2.1 authorization.
Is MCP's HTTP+SSE transport deprecated?
Yes. Streamable HTTP replaced it in 2025-03-26, and 2026-07-28 lists it as Deprecated.
How does MCP authorization work?
The server is an OAuth 2.1 resource server. It answers 401 with a pointer to RFC 9728 metadata; the client finds the authorization server, runs a PKCE code flow with an RFC 8707 resource parameter, checks iss, and sends a bearer token on every request.
MCP vs A2A: which should I use?
MCP connects a host to tools and data; A2A lets independent agents delegate tasks to each other. Many designs use both.
How do I see which MCP servers Claude Code is using?
Run claude mcp list for every configured server and its status, claude mcp get for one server, or /mcp inside a session.
Fix it without waiting for upstream
Most MCP security bugs live not in the specification but in an SDK, a server package or the HTTP, JSON and OAuth libraries beneath them, which makes them ordinary dependency problems.
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
These demos show MCP and agent controls in practice, not an MCP traffic filter. Download the CLI from https://safeguard.sh/download and read the setup guide at https://docs.safeguard.sh/docs/cli .
The first demo connects Claude to the Safeguard MCP server with OAuth and chooses which tools Claude can use. It is this guide's authorization flow in a real client, ending in a deliberately limited tool list.
Click through it at your own pace, or open it full screen.
The second demo is a real Safeguard CLI session. Asked to fix the lodash CVE in a repo, it reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, prints a summary and pushes nothing, then shows /permissions, /model and /scan. The same flow applies when an MCP SDK or one of its dependencies needs an upgrade, with a person approving each step.
Click through it at your own pace, or open it full screen.
The third demo sets up the local runner on your own machine, approves it with its device code, then scans from the terminal. If you build local MCP servers, scanning them where they run keeps their dependency trees visible.
Click through it at your own pace, or open it full screen.
Quick check
Your MCP server receives a request whose bearer token was issued for a different MCP server. What should it do?
Reject it with a 401. The authorization spec requires servers to accept only tokens issued for their own canonical URI and forbids accepting or passing through any other token. Audience binding through RFC 8707 resource indicators exists to make this check possible.
In MCP 2026-07-28, how does a server ask the user for input in the middle of a tool call?
It returns a result with resultType set to input_required, carrying inputRequests and optionally an opaque requestState. The client gathers the input and retries the original request with inputResponses. Servers no longer send their own JSON-RPC requests to the client.
Sources
- MCP Specification 2026-07-28: overview
- MCP 2026-07-28: Key Changes (changelog)
- MCP 2026-07-28: Deprecated Features registry
- MCP 2026-07-28: Base protocol overview
- MCP 2026-07-28: Versioning and Compatibility
- MCP 2026-07-28: stdio transport
- MCP 2026-07-28: Streamable HTTP transport
- MCP 2026-07-28: Authorization
- MCP 2026-07-28: Authorization Server Discovery
- MCP 2026-07-28: Client Registration
- MCP 2026-07-28: Authorization Security Considerations
- MCP 2026-07-28: Multi Round-Trip Requests
- MCP 2026-07-28: Subscriptions
- MCP 2026-07-28: Discovery
- MCP 2026-07-28: Tools
- MCP 2026-07-28: Resources
- MCP 2026-07-28: Prompts
- MCP 2026-07-28: Elicitation
- MCP 2026-07-28: Sampling
- MCP 2026-07-28: Roots
- MCP 2025-11-25: Lifecycle
- MCP 2025-11-25: Key Changes
- MCP 2025-06-18: Key Changes
- MCP 2025-03-26: Key Changes
- MCP: Security Best Practices
- MCP: Local Server Security
- MCP Inspector: CLI client
- JSON-RPC 2.0 Specification
- RFC 9728: OAuth 2.0 Protected Resource Metadata
- RFC 8707: Resource Indicators for OAuth 2.0
- Claude Code: Connect Claude Code to tools via MCP
- Claude API: MCP connector
- A2A Protocol Specification
Last reviewed: 23 October 2026. MCP revisions change quickly; check modelcontextprotocol.io for the current specification.