MCP Specification 1.2 — Remote Servers and Authentication

Complete reference guide to MCP Spec 1.2's remote server support with standardized OAuth 2.1 authentication. Covers the auth flow, migration path from local to remote servers, Streamable HTTP transport, and implications for agent architecture.

June 15, 2026
mcpmodel-context-protocoloauthauthenticationremote-serversstreamable-httpmcp-specreference-guidemigration-guideagent-architecture

What Changed in MCP Spec 1.2

The defining change in MCP Specification 1.2 (ratified June 2026) is standardized remote server support with OAuth 2.1-based authentication. Before 1.2, every MCP server shipped as a local subprocess — a stdio pipe from your AI client. This worked for development and single-user setups but fell apart at production scale: no access control, no multi-tenancy, no way to share a server across a team or expose internal APIs to AI agents safely.

Spec 1.2 fixes this by defining how an MCP server communicates over HTTP, how clients discover and authenticate to remote servers, and how authorization flows work end-to-end. The result is that MCP servers can now deploy as network services — same protocol semantics, same tool/resource/prompt abstractions, but accessible over the wire with proper auth.

Key Changes at a Glance

ComponentBefore 1.2After 1.2
Transportstdio (local subprocess) or HTTP+SSE (two endpoints)Streamable HTTP — single endpoint, POST for requests, optional SSE for streaming
AuthenticationNone (local stdio) or ad-hoc API keysOAuth 2.1 + PKCE — mandatory for HTTP transports
Client RegistrationManual API key provisioningDynamic Client Registration (RFC 7591)
Token BindingNoneResource indicators (RFC 8707) — tokens bound to specific server URIs
DiscoveryNoneAuthorization Server Discovery via RFC 9728 / RFC 8414 metadata endpoints
Scope ManagementNoneScoped permissions — read, write, admin per tool/resource
Session HandlingNone (single-request)Mcp-Session-Id header with Last-Event-ID resumption

Streamable HTTP — The Transport That Makes Remote Possible

Spec 1.2 replaces the old dual-endpoint transport (one SSE stream for server-to-client, one POST endpoint for client-to-server) with Streamable HTTP, a single MCP endpoint at a well-known path.

How It Works

POST https://mcp.mycompany.com/mcp
Authorization: Bearer ***
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "query_database",
    "arguments": { "sql": "SELECT count(*) FROM users" }
  },
  "id": 1
}

The server responds with either:

  • A single JSON response (Content-Type: application/json) for synchronous operations
  • An SSE stream for operations that produce streaming output (progress notifications, partial results, the final response)

Clients open an SSE channel via GET to the same endpoint for server-initiated messages. Sessions are tracked via the Mcp-Session-Id header, and clients use Last-Event-ID to resume after network drops.

Migration Path from SSE to Streamable HTTP

If you're running the legacy HTTP+SSE transport, the migration is straightforward:

  1. Add the single MCP endpoint at a path like /mcp or / that accepts POST and GET
  2. Support both transports during transition — the spec includes a backwards-compatibility section allowing servers to host both /sse (old) and /mcp (new)
  3. Negotiate protocol version — clients send MCP-Protocol-Version: 2025-06-18 (or later); fall back to SSE if the server returns a 4xx
  4. Drop legacy endpoints once all your clients have updated

Every major client now negotiates the Streamable HTTP protocol by default. SSE-only servers will drop off the long tail by end of 2026.

The Authorization Flow End-to-End

Spec 1.2 mandates OAuth 2.1 with PKCE for all HTTP-based MCP transports. Here's how it works in practice, step by step.

Step 1: Authorization Server Discovery

Before any authentication happens, the client discovers the server's authorization configuration. The MCP server exposes a Protected Resource Metadata endpoint (RFC 9728) that includes the authorization server URL:

GET https://mcp.mycompany.com/.well-known/oauth-protected-resource

Response:

{
  "authorization_servers": ["https://auth.mycompany.com"],
  "resource": "https://mcp.mycompany.com"
}

The client then fetches the authorization server's metadata (RFC 8414):

GET https://auth.mycompany.com/.well-known/oauth-authorization-server

Response:

{
  "issuer": "https://auth.mycompany.com",
  "authorization_endpoint": "https://auth.mycompany.com/authorize",
  "token_endpoint": "https://auth.mycompany.com/token",
  "registration_endpoint": "https://auth.mycompany.com/register",
  "scopes_supported": ["read", "write", "admin"],
  "response_types_supported": ["code"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["none"]
}

This discovery step is what makes the flow zero-config from the user's perspective — the client learns everything it needs from the server's metadata.

Step 2: Dynamic Client Registration (RFC 7591)

MCP clients (Claude Desktop, Claude Code, Cursor, VS Code, ChatGPT) cannot pre-register with every authorization server in the world. Dynamic Client Registration (DCR) solves this by letting the client register itself programmatically on first contact:

POST https://auth.mycompany.com/register
Content-Type: application/json

{
  "client_name": "Claude Desktop",
  "redirect_uris": ["http://localhost:8400/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

The server responds with a client_id:

{
  "client_id": "abc123",
  "client_id_issued_at": 1718496000,
  "client_secret": "",
  "client_secret_expires_at": 0
}

DCR is the mechanism that makes MCP servers plug-and-play. Without it, every server would require manual OAuth app creation — the exact friction MCP exists to remove.

Caveat for implementers: Dynamic registration means any client can register with your server. For internal servers, restrict registration to known origins or require a pre-shared registration token. For public servers, scope enforcement is your safety net.

Step 3: Authorization Request with PKCE

With a client_id in hand, the client starts the authorization code flow. PKCE (Proof Key for Code Exchange, RFC 7636) is mandatory for all clients — public and confidential alike.

The client generates a code_verifier and derives a code_challenge:

import hashlib
import base64
import secrets

code_verifier = secrets.token_urlsafe(32)
code_challenge = base64.urlsafe_b64encode(
    hashlib.sha256(code_verifier.encode()).digest()
).rstrip(b"=").decode()

Then redirects the user to the authorization endpoint:

GET https://auth.mycompany.com/authorize?
  response_type=code
  &client_id=abc123
  &redirect_uri=http://localhost:8400/callback
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256
  &resource=https%3A%2F%2Fmcp.mycompany.com
  &state=xyz789

Note the resource parameter — that's RFC 8707, binding the token to this specific MCP server. This prevents token replay against a different server.

The user authenticates (login page, SSO redirect, or web3 wallet), and the authorization server redirects back with a code:

HTTP/1.1 302 Found
Location: http://localhost:8400/callback?code=***&state=xyz789

Step 4: Token Exchange

The client exchanges the authorization code for an access token and refresh token:

POST https://auth.mycompany.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=http://localhost:8400/callback
&client_id=abc123
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&resource=https%3A%2F%2Fmcp.mycompany.com

The server verifies the code_verifier against the stored code_challenge, validates the resource parameter matches the MCP server URI, and returns tokens:

{
  "access_token": "eyJhbG...NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
  "scope": "read write"
}

Step 5: Making Authenticated Requests

Every subsequent MCP request includes the access token as a Bearer token:

POST https://mcp.mycompany.com/mcp
Authorization: Bearer ***
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "id": 1
}

Step 6: Token Refresh

When the access token expires (typically after one hour), the client uses the refresh token to get a new pair:

POST https://auth.mycompany.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&client_id=abc123
&resource=https%3A%2F%2Fmcp.mycompany.com

The spec requires refresh token rotation for public clients — each refresh invalidates the previous token and issues a new one. This limits the damage window if a refresh token leaks.

Scoped Permissions

MCP Spec 1.2 introduces standard OAuth scopes to gate access to specific tools and resources. The spec doesn't prescribe a naming convention, but the ecosystem has converged on:

Scope PatternPurpose
readRead-only tool access (queries, listing)
writeMutating operations
adminConfiguration, user management, destructive operations
read:<resource>Tool-specific read scopes
write:<resource>Tool-specific write scopes

Scope Enforcement at the Server

When an MCP server receives a request, it validates the token's scopes before executing the tool. A token with only read scope calling a write tool should get an insufficient_scope error:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "Insufficient scope",
    "data": {
      "required_scope": "write",
      "token_scopes": ["read"]
    }
  },
  "id": 1
}

Step-Up Authorization

If a client holds a read token but attempts a write operation, the server can challenge for elevated scopes. The client responds by initiating a step-up authorization flow — the user re-authenticates with the authorization server to grant additional scopes.

Security Considerations

Remote MCP servers introduce a fundamentally larger attack surface than local subprocesses. These are the threats the spec addresses and the ones that still need your attention in implementation.

What the Spec Gets Right

  • Audience binding (RFC 8707): Tokens are bound to a specific MCP server URI, preventing token replay across servers
  • PKCE is mandatory: Even public clients (desktop apps) must use proof keys, preventing authorization code interception
  • No implicit grant: The implicit flow and resource owner password credentials are explicitly forbidden
  • No bearer tokens in query strings: Token transmission is restricted to the Authorization header
  • Refresh token rotation: Limits exposure from leaked refresh tokens
  • Prohibits token passthrough: The spec states verbatim: "MCP servers MUST NOT pass through the token it received from the MCP client"

What You Still Need to Handle

Token passthrough is the #1 implementation mistake. The most common error is receiving a client's Bearer token and forwarding it to a downstream API (Slack, GitHub, Stripe). This breaks audience binding and creates a confused-deputy problem — the downstream API trusts the token because it's signed, not realizing it was minted for an entirely different resource. Issue your own token at the MCP server boundary. Validate it locally, then exchange for a separate downstream token using your own credentials.

Prompt injection surface grows with remote tools. Every remote tool you expose becomes an injection vector. An attacker who convinces an agent to call your tool with crafted arguments can exploit SQL injection, command injection, or SSRF vulnerabilities on your server. Apply the same input sanitization and parameterization you would for any public API. Treat every tool argument as untrusted user input.

Audience-claim laxness. If your server validates JWT signatures but not the aud (audience) claim, an attacker can present a token issued for other-server.example.com and gain access. Validate aud, iss, and exp on every request.

Origin-header DNS rebinding. A local MCP server that binds to 0.0.0.0 without Origin validation can be exploited by a malicious website through DNS rebinding. The spec mandates Origin validation and localhost-only binds for local servers.

Revocation propagation. The spec defines token revocation endpoints, but the semantics are soft: when a user revokes an app from the IDP's consent screen, the refresh token is invalidated immediately, but the existing access token keeps working until it expires. For instant revocation, you need short token TTLs (measured in seconds) or token introspection (RFC 7662) on every request.

Migration Guide: Local to Remote MCP Server

The most common deployment scenario in mid-2026 is converting an existing local MCP server to a remote one. Here's the checklist.

Prerequisites

  • An MCP server that currently runs as a stdio subprocess
  • A deployment target (your own infrastructure, Cloudflare Workers, Railway, Fly.io)
  • An OAuth 2.1 provider (Auth0, Okta, Keycloak), or a managed auth service (Cloudflare workers-oauth-provider, ScaleKit, Stytch)
  • TLS certificate for your deployment domain

Migration Steps

1. Switch transport from stdio to Streamable HTTP. Wrap your existing tool handlers behind an HTTP server that accepts POST requests to a single /mcp (or root) endpoint. Your tool logic stays the same — only the I/O layer changes.

2. Add the metadata endpoints. Serve /.well-known/oauth-protected-resource (RFC 9728) and link to your authorization server's metadata. This is what makes discovery work without manual configuration.

3. Configure Dynamic Client Registration. Set up the /register endpoint that accepts client registration requests. Your provider library (see options below) handles this for you.

4. Implement the authorization code flow. If you're not using a managed provider, implement:

  • /authorize — renders a login/consent screen, returns code
  • /token — exchanges codes for tokens, handles refresh
  • /.well-known/oauth-authorization-server — metadata document

5. Add token validation middleware. Before your tool handlers execute, validate the Bearer token: check signature, issuer, audience, expiration, and scopes. Reject unauthenticated requests with 401 Unauthorized.

6. Add scope checks to each tool. Review each tool handler and annotate it with the minimum scope required. Enforce at runtime.

7. Deploy with TLS. Remote MCP servers must run behind HTTPS. Tokens in cleartext are a compliance violation.

8. Update your clients. Point clients at https://mcp.mycompany.com/mcp instead of mcp-server --port 3100. The first connection triggers the auth flow.

Reference Implementations

If you don't want to build the OAuth plumbing from scratch:

  • Cloudflare workers-oauth-provider (workers-oauth-provider) — the OAuth 2.1 provider library used by Cloudflare's own remote MCP examples. Stores secrets only as hashes, wraps your fetch handler so your tool code receives an already-authenticated user.
  • Sentry MCP server (sentry-mcp) — open-source TypeScript Streamable-HTTP server with OAuth, deployed to mcp.sentry.dev. Good reference for how a production SaaS MCP server is structured.
  • FastMCP (gofastmcp.com) — Python framework with built-in OAuthProvider that handles metadata endpoints, DCR, token validation, and scope enforcement.

Implications for Agent Architecture

MCP going remote changes how architects think about tool distribution.

Centralized vs. Distributed Tool Servers

Before 1.2, every MCP server was a local process. An agent's config file listed a dozen mcpServers, and each one spawned as a child process. This worked but created per-client overhead — every instance of every agent had its own copy of every server.

With remote MCP, you can deploy a single shared tool server that multiple agents access simultaneously. This means:

  • Unified access control — manage who can call which tools from one place
  • Centralized audit logging — every tool invocation routes through your auth layer
  • Reduced memory overhead — one server instance instead of N copies
  • Consistent tool versions — update once, all agents get the new version

The Gateway Pattern

The MCP ecosystem is converging on a gateway architecture where a single ingress point handles auth, routing, and load balancing for a fleet of MCP servers:

User Agent → MCP Gateway (auth, routing) → Tool Server A
                                          → Tool Server B
                                          → Tool Server C

Cloudflare's workers-oauth-provider, ScaleKit, Stytch Connected Apps, and Composio Strata all follow this pattern. The gateway offloads the OAuth 2.1 + DCR + metadata plumbing so individual tool servers only ship business logic.

When to Use Each Approach

ScenarioApproachWhy
Single-user developer toolsLocal stdioZero latency, no auth overhead
Team sharing internal toolsRemote, single serverCentralized access, shared state
SaaS product exposing APIs via MCPRemote, gateway-delegated authMulti-tenant, scoped, auditable
Enterprise with 10+ MCP serversRemote, gateway + fleet managementOAuth config complexity grows with N servers

Comparison with Other Remote Tool Protocols

DimensionMCP Spec 1.2OpenAI Function CallingAnthropic Tool Use
TransportStreamable HTTP (single endpoint)HTTPS POST to /v1/chat/completionsHTTPS POST to /v1/messages
AuthOAuth 2.1 + PKCE (mandatory)API key (Bearer token)API key (x-api-key header)
Client registrationDynamic (RFC 7591)Static API key provisioningStatic API key provisioning
Token bindingResource indicators (RFC 8707)None (same key for all endpoints)None
Scope modelOAuth scopes (read/write/admin)No formal scope modelNo formal scope model
RefreshRefresh token rotationAPI key rotation (manual)API key rotation (manual)
Server-initiated messagesYes (SSE on GET)NoNo
Tool discoverytools/list (JSON-RPC)Schema passed in chat requestSchema passed in chat request
Multi-tenancyBuilt-in (OAuth per user)Manual (per-user API keys)Manual (per-user API keys)
RevocationToken endpoint revocationKey invalidationKey invalidation

MCP Spec 1.2's advantage is that it treats remote tool servers as first-class network services rather than thin wrappers around existing APIs. The OAuth 2.1 integration provides enterprise-grade auth that scales from single-user to multi-tenant deployments. The tradeoff is implementation complexity — OpenAI and Anthropic's approaches are simpler because they defer auth and scope management to the provider's API key model.

Pitfalls

DCR without validation. Dynamic client registration means anyone can register. If you're deploying an internal MCP server, add a whitelist or pre-shared registration token. Without this, your server is open to any client that discovers the endpoint.

Token passthrough to downstream APIs. The spec forbids it. The mistake is so common that nearly every MCP security audit in 2026 finds at least one server doing it. Validate the token locally, then issue a separate call to any downstream service.

Clock skew between auth server and MCP server. JWT validation checks exp and nbf. If your auth server and MCP server have drifting clocks (common in containerized deployments), valid tokens get rejected. Run NTP everywhere and verify.

Redirect URI handling for desktop clients. Desktop apps can't receive HTTP redirects to a public URL. The standard approach is starting a temporary local HTTP server on localhost:8400 (or a random port), using that as the redirect URI, and shutting it down after receiving the callback. Handle port conflicts gracefully.

Token storage across sessions. Users don't want to re-authenticate every time they restart their MCP client. On macOS, use Keychain. On Windows, use Credential Manager. On Linux, the situation is less standardized — some clients fall back to encrypted files in the home directory. Get this right early because users will notice immediately if they have to log in every time.

Scope naming consistency. Teams with multiple MCP servers end up with ad-hoc scope names (read:database, db-read, query). Adopt a naming convention before your second server deploys. The emerging standard is resource-type:scope (e.g., database:read, database:write, admin:*).

Quick Reference: Auth Endpoints

EndpointPurposeSpec Reference
/.well-known/oauth-protected-resourceProtected resource metadataRFC 9728
/.well-known/oauth-authorization-serverAuthorization server metadataRFC 8414
/registerDynamic client registrationRFC 7591
/authorizeAuthorization code requestOAuth 2.1 Section 4.1
/tokenToken exchange and refreshOAuth 2.1 Section 4.1.4
/revokeToken revocationRFC 7009
/mcp (or /)Streamable HTTP MCP endpointMCP Spec 1.2 Transports