@modelcontextprotocol/sdk) is the most capable TypeScript option: it can be a full OAuth authorization server with dynamic client registration, or a plain resource server that verifies bearer tokens. Either way it serves Streamable HTTP, so it fits any of MCP Manager’s three authentication modes. This page covers the decisions and gotchas for running it behind MCP Manager — the SDK’s own docs and source are authoritative. It also covers Vercel’s mcp-handler for Next.js at the end.
Serve Streamable HTTP — never SSE-only
UseStreamableHTTPServerTransport (from @modelcontextprotocol/sdk/server/streamableHttp.js). For a stateless deployment behind a load balancer, construct it with sessionIdGenerator: undefined; for stateful sessions, supply a generator (the transport then issues an Mcp-Session-Id). The SDK also ships a legacy SSEServerTransport for the old two-endpoint transport — don’t ship that as your only transport. See the server guide.
Match the SDK’s auth to an MCP Manager mode
The SDK splits cleanly into authorization-server mode and resource-server mode. Pick the one that matches your chosen MCP Manager mode.| You want | Use in the SDK | Notes |
|---|---|---|
| Standard OAuth + DCR | mcpAuthRouter (mounts the authorization-server endpoints, including /register) | /register only appears if your clientsStore.registerClient is implemented |
| OAuth + DCR proxying an IdP | ProxyOAuthServerProvider with endpoints.registrationUrl set | Forwards dynamic registration to your upstream IdP |
| Pre-registration / token | mcpAuthMetadataRouter + requireBearerAuth({ verifier, requiredScopes, resourceMetadataUrl }) | Resource-server only: you verify tokens an external authorization server issued |
clientsStore.registerClient exists — so dynamic registration is opt-in by implementing it. See the SDK’s src/server/auth/ (router, providers, middleware).
Get the metadata path right
The SDK serves protected-resource metadata at a path-suffixed well-known URL when your MCP endpoint isn’t at the root — for example/.well-known/oauth-protected-resource/mcp for an endpoint at /mcp. Use the SDK’s getOAuthProtectedResourceMetadataUrl(serverUrl) helper to build it rather than hand-writing the path, and make sure the advertised issuer and resource are your public HTTPS URL, not an internal host. A mismatch here is what makes MCP Manager fall back to manual entry or reject tokens.
Vercel mcp-handler (Next.js)
If you’re shipping an MCP server as Next.js route handlers on Vercel, mcp-handler wraps the SDK with createMcpHandler(...). It is a resource server only — it verifies tokens with withMcpAuth(handler, verifyToken, options) and serves RFC 9728 protected-resource metadata, but it does not implement an authorization server or dynamic client registration.
That maps to MCP Manager’s pre-registration or token-in-header modes: an external authorization server (or a static token) handles credentials, and mcp-handler validates them. See mcp-handler authorization docs.
MCP Manager compatibility checklist
Streamable HTTP at a fixed path
StreamableHTTPServerTransport at a
stable path (/mcp); don’t ship
only SSEServerTransport.Stateless or shared session state
sessionIdGenerator: undefined
for stateless, or externalize
session state — don’t keep it in one
instance’s memory.For DCR, implement registerClient + a persistent store
mcpAuthRouter, implement
clientsStore.registerClient, and
back it (plus codes and tokens) with
a database or Redis — not the demo
in-memory Map.Public metadata, correct path suffix
oauth-protected-resource
(and, for an authorization server,
oauth-authorization-server) at
your public URL with the right path
suffix; advertise the canonical
resource.TypeScript gotchas
The demo client store is not production-grade
The demo client store is not production-grade
DemoInMemoryClientsStore is a Map for examples. On more than one instance it loses MCP Manager’s registration between the register and authorize hops. Implement a persistent OAuthRegisteredClientsStore. See Debug Self-Hosted OAuth./register only exists if you implement registerClient
/register only exists if you implement registerClient
mcpAuthRouter mounts the
registration endpoint only when
clientsStore.registerClient is
defined. If MCP Manager can’t find a
registration_endpoint, that’s
usually why — implement it or use
pre-registration.Protected-resource metadata path suffix
Protected-resource metadata path suffix
/mcp, the metadata lives at /.well-known/oauth-protected-resource/mcp. Build it with getOAuthProtectedResourceMetadataUrl() rather than hand-rolling, or discovery fails..png?fit=max&auto=format&n=gKqTvJPtsRi2bLNx&q=85&s=8abbce3efb590630de2102c43d32aadf)
.png?fit=max&auto=format&n=Dy9YsIECUbR9JZiT&q=85&s=a1f404cd7f7aeb1727c89d81137ae1ac)