Remote MCP Servers and Authorization — 2026-07-28 Revision

Reference guide to remote MCP servers and the optional OAuth 2.1 authorization profile. Covers Streamable HTTP from 2025-03-26, the stateless 2026-07-28 revision, client registration changes, and migration.

October 19, 2026
mcpmodel-context-protocoloauthauthenticationremote-serversstreamable-httpmcp-specreference-guidemigration-guideagent-architecture

How Remote Servers Evolved

MCP is versioned by date, not by a major/minor number. Remote servers were not introduced by a "1.2" release; the relevant revisions are:

  • 2024-11-05 defined two transports: stdio for local subprocesses and HTTP+SSE for remote servers.
  • 2025-03-26 replaced HTTP+SSE with Streamable HTTP, a single MCP endpoint that accepts POST and may answer with JSON or a request-scoped SSE stream.
  • 2025-06-18 is the revision whose authorization page defines the optional OAuth 2.1 profile for HTTP transports.
  • 2026-07-28 made the protocol stateless: it removed the initialize handshake, protocol-level sessions, ping, and SSE resumability. The latest revision is 2026-07-28.

The result is that MCP servers can deploy as network services with the same tool/resource/prompt abstractions as local servers, while authorization remains optional and HTTP implementations SHOULD conform to the OAuth 2.1 profile when they support it.

Key Changes at a Glance

ChangeRevisionWhat it did
Remote HTTP transport2024-11-05HTTP+SSE with two endpoints
Streamable HTTP2025-03-26Single MCP endpoint replaces HTTP+SSE; optional SSE per request
Authorization framework2025-06-18Optional OAuth 2.1 profile for HTTP; PKCE required when used
Stateless requests2026-07-28Removed initialize/notifications/initialized, ping, and protocol-level sessions
Sessions and resumption2026-07-28Removed Mcp-Session-Id and Last-Event-ID
Client registration2026-07-28Deprecated Dynamic Client Registration in favor of Client ID Metadata Documents

Streamable HTTP — The Transport That Makes Remote Possible

Streamable HTTP was introduced in 2025-03-26, replacing the 2024-11-05 dual-endpoint transport (one SSE stream for server-to-client messages, one POST endpoint for client-to-server). The 2026-07-28 revision removed the GET stream endpoint and protocol-level sessions.

How It Works

POST https://mcp.mycompany.com/mcp
Authorization: Bearer ***
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: query_database

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "query_database",
    "arguments": { "sql": "SELECT count(*) FROM users" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  },
  "id": 1
}

The server responds with either:

  • A single JSON response (Content-Type: application/json) for synchronous operations
  • An SSE stream scoped to that request for operations that produce progress notifications or partial results

Long-lived change notifications are requested with subscriptions/listen, whose response stream stays open and carries only the notification types the client opted into. There is no session header and no resumption: a broken stream loses the in-flight request, and the client must re-issue it with a new request ID.

Migration Path from SSE to Streamable HTTP

If you're running the legacy HTTP+SSE transport:

  1. Add the single MCP endpoint at a path like /mcp or / that accepts POST
  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 the protocol version per request — send MCP-Protocol-Version: 2026-07-28 and the matching io.modelcontextprotocol/protocolVersion in _meta; fall back to the legacy handshake only when the server returns a non-MCP 400
  4. Drop legacy endpoints once all your clients have updated

The Authorization Flow End-to-End

Authorization is OPTIONAL in MCP. When an HTTP-based transport supports it, implementations SHOULD conform to the authorization framework added in 2025-06-18, which uses OAuth 2.1 with PKCE. Here's how the flow works in practice.

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: Client Registration (DCR deprecated)

Earlier revisions used Dynamic Client Registration (DCR, RFC 7591) so a client could register itself on first contact. The 2026-07-28 revision deprecates DCR as a client registration mechanism in favor of Client ID Metadata Documents: the client presents a stable client_id that is a URL resolving to a client-hosted metadata document. DCR remains available for backwards compatibility with authorization servers that do not support Client ID Metadata Documents.

For a legacy authorization server that only supports DCR, the client registers with:

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
}

New implementations should support Client ID Metadata Documents instead. DCR also means any client can register, so internal servers need a whitelist or pre-shared registration token.

Step 3: Authorization Request with PKCE

With a client_id in hand, the client starts the authorization code flow. When authorization is used, MCP clients MUST implement PKCE (Proof Key for Code Exchange, RFC 7636), for public and confidential clients 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 does not prescribe scope names or a scope taxonomy. The authorization server defines the scopes it issues, and the MCP server validates the token's scopes before executing a tool. A common deployment convention is read/write/admin scopes, but that is an implementation choice, not a protocol requirement.

Scope Enforcement at the Server

When a request lacks a required scope, the server responds with HTTP 403 Forbidden, per the authorization specification's error table. This is an HTTP authorization error, not a JSON-RPC error code.

HTTP/1.1 403 Forbidden

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 required when authorization is used: 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 client registration. Support Client ID Metadata Documents for new clients. Keep a /register endpoint only for backwards compatibility with Dynamic Client Registration, which 2026-07-28 deprecates.

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, client registration, token validation, and scope enforcement.

Implications for Agent Architecture

MCP going remote changes how architects think about tool distribution.

Centralized vs. Distributed Tool Servers

The 2024-11-05 revision already allowed remote servers over HTTP+SSE, but most deployments ran local subprocesses. An agent's config file listed a dozen mcpServers, and each one spawned as a child process. This 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 + client-registration + 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 2026-07-28OpenAI Function CallingAnthropic Tool Use
TransportStreamable HTTP (single endpoint)HTTPS POST to /v1/chat/completionsHTTPS POST to /v1/messages
AuthOptional OAuth 2.1 profile (HTTP)API key (Bearer token)API key (x-api-key header)
Client registrationClient ID Metadata Documents (DCR deprecated)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 messagesNo; input requests are embedded in resultsNoNo
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's remote-server model treats tool servers as first-class network services rather than thin wrappers around existing APIs. The optional OAuth 2.1 profile 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

Unrestricted client registration. Dynamic client registration means anyone can register, and it is deprecated in favor of Client ID Metadata Documents. If you're deploying an internal MCP server with DCR for backwards compatibility, add a whitelist or pre-shared registration token.

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 registration (deprecated; prefer Client ID Metadata Documents)RFC 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 2026-07-28 Transports

Sources