@
feat(web): SEO — server-rendered template pages + /guides articles - templates/[slug] converted from client to server component: per-template generateMetadata (title/description/canonical/OG) + SoftwareApplication JSON-LD; code-audit toggle split into a client island; missing/non-public templates now return a real 404. - sitemap.ts pulls public template slugs live from the API (best-effort) + the new /guides routes. - new /guides section: 3 server-rendered SEO articles (host MCP with OAuth, hosted-platforms comparison, MintMCP alternative) with TechArticle JSON-LD; Guides link added to the marketing nav. - lib/seo.ts: articleJsonLd + templateJsonLd builders. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
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 "confidential" 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 "working" 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user