
The Problem
There have been a lot of different evolutions of external function calling with this age of LLMs. First it was plugins, then it was tool calling, then it now is MCP (which looks like it is here to stay). But throughout all of that there has been one consistent issue, authentication.
How do you authenticate a request to make sure that a user isn’t just accessing someone else’s data. I had this same thought when I was working on one of my projects agenda.dev. I wanted users to be able to access their todos in a simple MCP fashion, I originally put out a tweet asking what the best way to implement it:
left or right for authorization on an MCP pic.twitter.com/aihl7WRZOR
— Ryan Vogel (@ryandavogel) May 8, 2025
Both of these presented a glaring issue, the auth uuid or bearer token would be available in plaintext via the mcp.json
of the client provider. This obviously was an issue as pointed out my Mark F:
Neither, use OAuth. No tokens in config.
— mark (@r_marked) May 8, 2025
This got me thinking, wait, why haven’t I heard of a MCP OAuth solution that is simple? Then I started doing some digging and found that there are some implementations for some other auth providers but not for Better Auth, which is what I was using in the agenda codebase.
Figuring out a Solution to MCPs with OAuth?
So I reached out to Bereket Engida, the founder of Better Auth and Andrew Qu from Vercel, the author of the @vercel/mcp-adapter
npm package which made it super easy to create an MCP route in a standard Next.js project.
Essentially, this package is a lifesaver for Next.js developers wanting their apps to communicate with AI models using the Model Context Protocol (MCP). It gives you a straightforward way to set up an MCP server right within your Next.js project. This means you can easily define ‘tools’ that your AI can use, like making your app roll dice or fetch specific data. The beauty of the @vercel/mcp-adapter
is that it handles a lot of the complex, behind-the-scenes plumbing needed for MCP, letting you focus on building cool features instead of wrestling with protocol details.
They were able to whip together a solution based on the Model Context Protocol (MCP) specification for authorization as seen below (pretty complex):

Adding the Better Auth and Vercel MCP handler
The implementation is super minimal with the user only having to modify 3 files with under 10 lines of code.
First off, in your auth.ts
file that manages your primary Better Auth instance config you need to add the MCP plugin
// auth.ts
import { betterAuth } from "better-auth";
import { mcp } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
mcp({
loginPage: "/sign-in" // path to your login page
})
]
});
Then you have to add the oauth-server route that is built in to the MCP auth specification
// .well-known/oauth-authorization-server/route.ts
import { oAuthDiscoveryMetadata } from better-auth/plugins";
import { auth } from "../../../lib/auth";
export const GET = oAuthDiscoveryMetadata(auth);
After that you can jump to api/[transport]/route.ts which contains the primary functions for the MCP server which looks like this before wrapping auth:
// app/api/[transport]/route.ts
import { createMcpHandler } from '@vercel/mcp-adapter';
const handler = createMcpHandler(
server => {
server.tool(
'roll_dice',
'Rolls an N-sided die',
{
sides: z.number().int().min(2)
},
async ({ sides }) => {
const value = 1 + Math.floor(Math.random() * sides);
return {
content: [{ type: 'text', text: `🎲 You rolled a ${value}!` }],
};
}
);
},
{
},
{
redisUrl: process.env.REDIS_URL,
basePath: '/api',
maxDuration: 60,
verboseLogs: true,
}
);
export { handler as GET, handler as POST };
And wrap it with the withMcpAuth
function to require auth for the entire MCP server or even reach into the users session to grab the userid to check access to resources (where in my use case would be making sure the user only accesses their todo’s)
This configuration section allows you to optionally connect to Redis for features like resilient Server-Sent Event (SSE) transport, define the base API path for your MCP endpoint, and set operational parameters like maximum connection duration and logging verbosity.
// app/api/[transport]/route.ts
import { auth } from "@/lib/auth";
import { createMcpHandler } from "@vercel/mcp-adapter";
import { withMcpAuth } from "better-auth/plugins";
import { z } from "zod";
const handler = async (req: Request) => {
const session = await auth.api.getMCPSession({
headers: req.headers
})
if(!session){
return new Response(null, {
status: 401
})
}
return createMcpHandler(
(server) => {
server.tool(
"echo",
"Echo a message",
{ message: z.string() },
async ({ message }) => {
return {
content: [{ type: "text", text: `Tool echo: ${message}` }],
};
},
);
},
{
capabilities: {
tools: {
echo: {
description: "Echo a message",
},
},
},
},
{
redisUrl: process.env.REDIS_URL,
basePath: "/api",
verboseLogs: true,
maxDuration: 60,
},
)(req);
}
export { handler as GET, handler as POST, handler as DELETE };
This was a super easy implementation that I was able to implement in agenda in less than 30 minutes, then after that you just have to configure your MCP server and the tools, after it was implemented all I had to do was add the correct endpoint in my editor of choice (which for this demo was Cursor)
"agenda": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://localhost:3000/api/mcp"
]
}
Testing the MCP in Cursor (with authentication)
What this will do now is when you open up the client and it hits that endpoint, it will check for authentication, if the user doesn’t have a session it will open a popup to the loginPage we set above and ask the user to login, once we are logged in and fully setup we should see the green light to start using it:

Now all we have to do is just see whats on our agenda 😃:

And just like that it lets you access your todos directly from the LLM client!
The Model Context Protocol is still very early and authentication, payments, and supported transports are still being actively discussed and hashed out. OAuth seems promising and it’s great to see more remote MCP servers, authentication providers, and infrastructure companies adopting it. Excited for what’s next in MCP!
You can check out the open source github repos for the MCP Adapter & Better Auth here:
MCP Adapter: https://github.com/vercel/mcp-adapter
Better Auth: https://github.com/better-auth/better-auth