2026-05-31 12:08:05 +02:00
import { JsonLd } from '@/components/json-ld' ;
2026-07-08 23:00:02 +02:00
import { articleJsonLd , breadcrumbJsonLd , pageMetadata } from '@/lib/seo' ;
2026-05-31 12:08:05 +02:00
import Link from 'next/link' ;
import { ArticleShell , H2 , P , Strong , UL } from '../article-shell' ;
const PATH = '/guides/host-mcp-server-with-oauth' ;
const TITLE = 'How to host a remote MCP server with OAuth (2026)' ;
const DESCRIPTION =
'What it actually takes to put a remote MCP server in production: Streamable HTTP transport, OAuth 2.1 with PKCE and Resource Indicators, and the shortcuts.' ;
export const metadata = pageMetadata ( { title : TITLE , description : DESCRIPTION , path : PATH } ) ;
export default function Page() {
return (
< >
< JsonLd
data = { articleJsonLd ( {
title : TITLE ,
description : DESCRIPTION ,
path : PATH ,
datePublished : '2026-05-31' ,
} ) }
/ >
2026-07-08 23:00:02 +02:00
< JsonLd
data = { breadcrumbJsonLd ( [
{ name : 'Home' , path : '/' } ,
{ name : 'Guides' , path : '/guides' } ,
{ name : TITLE , path : PATH } ,
] ) }
/ >
2026-05-31 12:08:05 +02:00
< ArticleShell
title = { TITLE }
subtitle = "Local STDIO servers are easy. A remote MCP server that Claude, Cursor and ChatGPT can install over the internet — without leaving it open to the world — is where the real work is. Here's the whole picture."
updated = "May 2026"
>
< H2 > Local vs remote : why this is harder than it looks < / H2 >
< P >
A local MCP server talks to one client over STDIO on your machine — no network , no auth .
The moment you want a server that lives at a URL and any MCP client can connect to , you
inherit a full web - service problem : a public transport , TLS , identity , authorization , and
isolation between callers . The MCP spec settled on < Strong > Streamable HTTP < / Strong > as the
remote transport ( it replaced the older HTTP + SSE pairing ) , and on { ' ' }
< Strong > OAuth 2.1 < / Strong > as the auth model . Both are non - negotiable if you want the
server installable from Claude Desktop or ChatGPT .
< / P >
< H2 > The OAuth 2.1 pieces you can ' t skip < / H2 >
< P >
MCP authorization is OAuth 2.1 , and for remote servers it leans on a few RFCs that older
OAuth tutorials don ' t cover :
< / P >
< UL >
< li >
< Strong > PKCE ( RFC 7636 ) < / Strong > on every authorization - code exchange — mandatory in
OAuth 2.1 , no exceptions for & quot ; confidential & quot ; clients .
< / li >
< li >
< Strong > Dynamic Client Registration ( RFC 7591 ) < / Strong > — clients like Claude Desktop
register themselves at runtime ; you can ' t pre - provision a client_id for every user .
< / li >
< li >
< Strong > Resource Indicators ( RFC 8707 ) < / Strong > — the token has to be bound to the
specific MCP server ( the < code > resource < / code > ) , so a token minted for one server can ' t
be replayed against another .
< / li >
< li >
< Strong > Protected - resource metadata < / Strong > — your server returns a { ' ' }
< code > WWW - Authenticate < / code > header pointing at the authorization server so clients can
discover where to get a token .
< / li >
< / UL >
< P >
Get any of these wrong and the symptom is the same : the client either can ' t complete the
handshake , or it silently fails to discover your auth server . This is the single most
common reason a & quot ; working & quot ; MCP server won ' t install from Claude .
< / P >
< H2 > Option A — roll your own on generic infra < / H2 >
< P >
You can deploy a remote MCP server to < Strong > Cloudflare Workers < / Strong > ( the most common
production choice , edge - global ) , or to < Strong > Render , Fly , or Cloud Run < / Strong > as a
normal container . Cloudflare even ships an OAuth provider library for Workers - based MCP
servers . This path gives you full control and is the right call if you have engineers and
want to own the runtime .
< / P >
< P >
The cost is everything around the code : standing up the authorization server ( or wiring a
third - party IdP correctly for the RFCs above ) , per - tenant secret storage , TLS , rate
limiting , and keeping the transport spec - current as MCP evolves . Budget days , not hours ,
for the auth layer alone .
< / P >
< H2 > Option B — a platform that wraps it for you < / H2 >
< P >
If you already have a server , tools like < Strong > MintMCP < / Strong > take a local STDIO
server and expose it as a remote one with OAuth wrapping . If you { ' ' }
< Strong > don 't have a server yet</Strong>, that' s where BuildMyMCPServer fits : you describe
the tool in plain language , it generates the TypeScript MCP server , runs static checks ,
builds a container , and deploys it behind a full OAuth 2.1 authorization server — PKCE ,
DCR and Resource Indicators included — with copy - paste install snippets for each client .
< / P >
< H2 > A practical checklist before you ship < / H2 >
< UL >
< li > Transport is Streamable HTTP , served over TLS at a stable public URL . < / li >
< li > Unauthenticated request returns 401 + a < code > WWW - Authenticate < / code > pointing at your AS . < / li >
< li > Authorization code flow enforces PKCE ( S256 ) , exact redirect - URI match , single - use codes . < / li >
< li > Issued access tokens are audience - bound to the specific server ( RFC 8707 ) . < / li >
< li > Per - caller secrets are encrypted at rest and injected only at runtime , never logged . < / li >
< li > You 've actually installed it from Claude Desktop end-to-end — not just curl' d it . < / li >
< / UL >
< P >
Whichever route you take , test the real install path in a real client early . See the { ' ' }
< Link href = "/guides/hosted-mcp-platforms-compared" className = "text-[--color-accent] hover:underline" >
platform comparison
< / Link > { ' ' }
for which option fits your situation .
< / P >
< / ArticleShell >
< / >
) ;
}