Skip to main content
The official MCP SDK gives you the bare minimum to build an MCP server. LeanMCP builds on top of it — the same way Next.js builds on React. You take the underlying protocol, build a framework on top, and make it production-ready. Building an MCP locally is easy. The official SDK works fine for that. But here’s the thing — when you want to add the real features that actually matter for production MCPs, it becomes really tough:
  • Authentication — connecting with Clerk, Auth0, Cognito, Firebase
  • Elicitation — collecting user input during tool execution
  • MCP UI & Apps — rendering components in clients
  • Remote deployment — running your MCP on a server
These are the features that separate a toy from a production service. And the official SDK leaves you completely on your own for all of them.

What LeanMCP Does

LeanMCP abstracts the hard parts. Instead of writing 600-700 lines of code just to set up authentication, you write 20-30 lines and run a few CLI commands. That’s it. We integrate with the auth providers you already use — Clerk, Auth0, AWS Cognito, Firebase, Google Cloud. We handle the JWT validation, JWKS fetching, token extraction, scope checking. You just add a decorator. And it’s entirely open source, just like the official MCP SDK. MIT license. Fork it, extend it, contribute to it.

Deployment: LeanMCP + LeanMCP Platform

Think of it like Next.js + Vercel. You can deploy Next.js anywhere — AWS, GCP, your own servers. But Vercel gives you optimized deployment, edge functions, and observability out of the box. Same with LeanMCP. You can deploy to any platform you want — AWS, GCP, Railway, Render — you’re not locked in. But if you want optimized deployment with built-in observability, monitoring, and zero-config setup, LeanMCP’s platform handles that for you with leanmcp deploy.

Why Opinionated Matters

MCP is a developing protocol. People don’t realize the places where they can go wrong. One example: developers often use elicitation to collect authentication tokens. Seems reasonable, right? But it’s a security vulnerability. When you use elicitation, the data passes through the MCP client — both the client and server see the token in plain text. If the client is compromised, the token is stolen. The correct approach is using the protocol’s _meta.authorization.token field, which is handled by the client and never visible in tool responses. Without guidance, you’d never know this. LeanMCP enforces these best practices by design — so you don’t accidentally expose vulnerabilities.

The Relationship

LeanMCP is built on top of the official SDK. It doesn’t replace it — it extends it.

Why Does This Matter?

Local Development is Easy Either Way

Building a basic MCP on your local machine is straightforward with either SDK:
// Official MCP SDK - works fine for basics
const server = new McpServer({ name: "my-server" });
server.tool("hello", "Say hello", { name: z.string() }, async ({ name }) => {
  return { content: [{ type: "text", text: `Hello, ${name}!` }] };
});
// LeanMCP - also simple
@Tool({ description: "Say hello" })
hello(input: { name: string }) {
  return `Hello, ${input.name}!`;
}
No significant difference for basic tools.

The Problem: Production Features

When you need real production features, the official SDK leaves you on your own:
FeatureOfficial MCP SDKLeanMCP
AuthenticationDIY (~600-700 lines)@leanmcp/auth (~20-30 lines)
ElicitationManual implementation@leanmcp/elicitation decorator
OAuth ProvidersBuild from scratchClerk, Auth0, Cognito, Firebase built-in
HTTP TransportBasicProduction-ready with session management
DeploymentManualleanmcp deploy

Code Comparison: Authentication

Official MCP SDK (~600-700 lines)

// You have to build everything yourself:
// 1. JWT validation
// 2. JWKS fetching and caching
// 3. Token extraction from requests
// 4. Scope validation
// 5. Error handling
// 6. Refresh token logic
// 7. Provider-specific quirks

import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const client = jwksClient({
  jwksUri: 'https://your-provider/.well-known/jwks.json',
  cache: true,
  rateLimit: true,
});

function getKey(header, callback) {
  client.getSigningKey(header.kid, (err, key) => {
    const signingKey = key?.getPublicKey();
    callback(null, signingKey);
  });
}

async function validateToken(token: string) {
  return new Promise((resolve, reject) => {
    jwt.verify(token, getKey, {
      issuer: 'https://your-provider/',
      audience: 'your-audience',
    }, (err, decoded) => {
      if (err) reject(err);
      else resolve(decoded);
    });
  });
}

// ... 500+ more lines for middleware, error handling, 
// scope checking, provider setup, etc.

LeanMCP (~20-30 lines)

import { AuthProvider, Authenticated } from "@leanmcp/auth";

const auth = new AuthProvider('clerk', {
  secretKey: process.env.CLERK_SECRET_KEY!
});

await auth.init();

@Authenticated(auth)
export class MyService {
  @Tool({ description: "Protected tool" })
  async getData() {
    // authUser available automatically
    return { userId: authUser.sub };
  }
}
3 CLI commands + 20-30 lines of code vs 600-700 lines of boilerplate.

Built-in Provider Support

LeanMCP integrates with popular auth providers out of the box:
ProviderSetup
Clerknew AuthProvider('clerk', { secretKey })
Auth0new AuthProvider('auth0', { domain, audience })
AWS Cognitonew AuthProvider('cognito', { userPoolId, region })
Firebasenew AuthProvider('firebase', { projectId })
Google Cloudnew AuthProvider('gcp', { projectId })
Customnew AuthProvider('custom', { jwksUri, issuer })
No need to learn each provider’s quirks — LeanMCP handles it.

Opinionated Best Practices

The MCP protocol is evolving. Without guidance, developers make mistakes that create security vulnerabilities.

Example: Auth Tokens in Elicitation

A common mistake is using elicitation to collect authentication tokens:
// ❌ DANGEROUS: Don't do this!
@Tool({ description: "Login" })
async login() {
  const token = await elicit({
    message: "Enter your API token",
    schema: { token: { type: "string" } }
  });
  
  // Now both client AND server have seen the token
  // If client is compromised, token is exposed
}
Why this is wrong:
  • Elicitation passes data through the MCP client
  • Client sees the token in plain text
  • If client is malicious or compromised, token is stolen
The correct approach: Authentication tokens should flow through the MCP protocol’s _meta.authorization.token field — handled by the client, never visible in tool responses. LeanMCP enforces this pattern automatically with @Authenticated.

Both Are Open Source

Official MCP SDKLeanMCP
LicenseMITMIT
Sourcegithub.com/modelcontextprotocolgithub.com/Leanmcp-Community
FoundationProtocol implementationFramework on top
LeanMCP is entirely open source. You can fork it, extend it, or contribute to it.

When to Use Each

Use Official MCP SDK if:

  • Building a simple, local-only MCP
  • You want full control over everything
  • You’re experimenting or learning the protocol
  • You don’t need auth, elicitation, or deployment

Use LeanMCP if:

  • Building for production
  • You need authentication (Clerk, Auth0, Cognito, etc.)
  • You want elicitation with proper validation
  • You want to deploy remotely
  • You prefer convention over configuration
  • You want security best practices enforced

Summary

AspectOfficial MCP SDKLeanMCP
PhilosophyMinimal, DIYOpinionated, batteries-included
AuthBuild yourselfBuilt-in providers
ElicitationManualDecorators
DeploymentManualleanmcp deploy
Code for auth~600-700 lines~20-30 lines
Best practicesYour responsibilityEnforced by framework
LeanMCP is to MCP SDK what Next.js is to React — same foundation, more structure, faster to production.