In this article
July 8, 2025
July 8, 2025

The Vercel MCP + WorkOS AuthKit template: deploy secure MCP servers globally in 5 minutes

We built an MCP server template with Vercel's MCP adapter and secured by WorkOS AuthKit that you can use to rapidly deploy secured MCP servers globally.

What if adding auth to your AI tools on Vercel Edge was as easy as a Next.js API route?

As AI agents go mainstream, developers need auth that fits the Next.js model—not a detour into middleware hell, brittle JWT plumbing, or stateful session bloat. Most approaches break the simplicity that makes Vercel so compelling. This one doesn’t.

The authHandler Pattern

The WorkOS Vercel MCP Example demonstrates the authHandler pattern.

Instead of building authentication into your business logic, you wrap your entire MCP server with a single function that handles all the complexity:

// 1. Build MCP server with tool modules
import { createMcpHandler, experimental_withMcpAuth } from '@vercel/mcp-adapter';
import { registerPublicTools } from '@/lib/mcp/tools/public';
import { registerExampleTools } from '@/lib/mcp/tools/examples';

const handler = createMcpHandler((server) => {
  registerPublicTools(server);    // ping, status - no auth needed
  registerExampleTools(server);   // user data - auth required
});

// 2. Wrap with WorkOS authentication  
const authHandler = experimental_withMcpAuth(
  handler,
  async (request, token) => {
    if (!token) return undefined; // Allow public tools
    
    const { payload } = await jwtVerify(token, JWKS);
    const userProfile = await workos.userManagement.getUser(payload.sub);
    
    return {
      token,
      clientId: process.env.WORKOS_CLIENT_ID!,
      scopes: [],
      extra: { user: userProfile, claims: payload }
    };
  },
  { required: false } // Tools decide individually
);

// 3. Export as Next.js API route
export { authHandler as GET, authHandler as POST };

Your tools then decide if they need authentication:

// Public tool - works for everyone
server.tool('ping', 'Health check', {}, async (args, { authInfo }) => {
  const isAuth = isAuthenticated(authInfo);
  return { result: 'pong', authenticated: isAuth };
});

// Private tool - requires authentication  
server.tool('createExampleData', 'Creates user data', schema, async (args, { authInfo }) => {
  const user = ensureUserAuthenticated(authInfo); // Throws if not authenticated
  return await createExampleData(args, user);
});

That's it!

Your MCP server now has enterprise JWT authentication, automatic user context, SSO support, and can be deployed to Vercel Edge with zero configuration.

Why This Matters for Developers

Clean Separation of Concerns


Your business logic stays pure. Authentication is handled declaratively by the wrapper, not scattered throughout your codebase.

Want to change auth providers? Change one function. Need to add new business tools? They automatically inherit the authentication system.

Mixed Authentication by Design


The pattern elegantly handles both public and private tools in the same server.

Public tools (like `ping` or `status`) work for everyone, while private tools (like `getUserData` or `createReport`) require authentication.

No complex routing or multiple servers needed.

What makes this pattern particularly elegant is how it leverages the MCP protocol's design.

The authInfo parameter flows naturally through the system, arriving in your tools exactly when and where you need it.

No global state, no complex middleware chains—just clean, functional composition.

The ensureUserAuthenticated helper demonstrates this:

// lib/auth/helpers.ts - Our custom WorkOS wrapper
export const ensureUserAuthenticated = (authInfo: AuthInfo | undefined): User => {
  if (!authInfo?.extra) {
    throw new Error('Authentication required for this tool');
  }
  
  // Extract WorkOS user from the authInfo.extra (set by our verifyToken function)
  const workosAuth = authInfo.extra as WorkOSAuthInfo;
  if (!workosAuth || !workosAuth.user) {
    throw new Error('Authentication required for this tool');
  }
  return workosAuth.user; // Return the WorkOS user object
};

Tools that need authentication call this helper. Tools that don't need authentication simply ignore the `authInfo` parameter. It's optional authentication done right.

Enterprise-Ready in Five Minutes


WorkOS AuthKit provides:

  • SSO integration with Google, Microsoft, and SAML providers
  • User management APIs for profile data and permissions
  • Compliance features your security team expects
  • Scalable architecture that grows with your organization

Launch Your Own Secure MCP Server and Test Immediately


The example includes a complete testing interface built into the web app. You can test both public and authenticated endpoints, see exactly how the JWT flow works, and verify your tools behave correctly—all without configuring external MCP clients.

Get Started

The repository includes everything you need:

1. Complete implementation with public and private example tools showing different patterns
2. Test interface for immediate experimentation and verification
3. TypeScript throughout for type safety and great developer experience
4. One-click Vercel deployment for production readiness

Clone it, add your WorkOS credentials, and you're running an authenticated MCP server in under 5 minutes.

This site uses cookies to improve your experience. Please accept the use of cookies on this site. You can review our cookie policy here and our privacy policy here. If you choose to refuse, functionality of this site will be limited.