buildmymcpserver/apps/web/app/(marketing)/guides/host-mcp-server-with-oauth/page.tsx

117 lines
5.8 KiB
TypeScript
Raw Normal View History

import { JsonLd } from '@/components/json-ld';
import { articleJsonLd, pageMetadata } from '@/lib/seo';
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',
})}
/>
<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>
</>
);
}