leanmcp
LeanMCP SDK - TypeScript SDK for building Model Context Protocol (MCP) servers with type-safe decorators
Installation
npx leanmcpAsk AI about leanmcp
Powered by Claude Β· Grounded in docs
I know everything about leanmcp. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
LeanMCP SDK: Production-Ready Infra for MCP Servers.
Build and Deploy Scalable MCP Servers with Full Backend Capabilities
Production-grade support:
β Authentication β Multi-tenant isolation β Request logging β Observability & Monitoring β Private Cloud deployment
Ideal for teams building:
- Agent platforms
- Customer-facing intelligent workflows
- Multi-tenant SaaS AI systems
Links
- Docs: https://docs.leanmcp.com
- Build & Deploy: https://ship.leanmcp.com
- Observability Platform: https://app.leanmcp.com
- npm packages: https://www.npmjs.com/search?q=%40leanmcp
- GitHub org: https://github.com/LeanMCP
Thanks for your Interest !
If you're:
- An Infra / AI / DevTool developer β you're warmly welcome to contribute ideas or code.
- Building your own Agent platform β reach out for enterprise-grade deployment support.
For partnerships & business inquiries: founders@leanmcp.com
If you find this project valuable, please consider giving us a GitHub star π !
Table of Contents
- Quick Start
- Installation & Packages
- Core Concepts
- Common Patterns
- Detailed Reference
- Contributing
- Business Collaboration
Quick Start
1. Create a new project
npx @leanmcp/cli create my-mcp-server
cd my-mcp-server
npm install
This generates a clean project structure:
my-mcp-server/
βββ main.ts # Entry point with HTTP server
βββ package.json # Dependencies
βββ tsconfig.json # TypeScript config
βββ mcp/ # Services directory
βββ example/
βββ index.ts # Example service
2. Define your service
The generated mcp/example/index.ts shows class-based schema validation:
import { Tool, Optional, SchemaConstraint } from "@leanmcp/core";
// Define input schema as a TypeScript class
class AnalyzeSentimentInput {
@SchemaConstraint({
description: 'Text to analyze',
minLength: 1
})
text!: string;
@Optional()
@SchemaConstraint({
description: 'Language code',
enum: ['en', 'es', 'fr', 'de'],
default: 'en'
})
language?: string;
}
// Define output schema
class AnalyzeSentimentOutput {
@SchemaConstraint({ enum: ['positive', 'negative', 'neutral'] })
sentiment!: string;
@SchemaConstraint({ minimum: -1, maximum: 1 })
score!: number;
@SchemaConstraint({ minimum: 0, maximum: 1 })
confidence!: number;
}
export class SentimentService {
@Tool({
description: 'Analyze sentiment of text',
inputClass: AnalyzeSentimentInput
})
async analyzeSentiment(args: AnalyzeSentimentInput): Promise<AnalyzeSentimentOutput> {
const sentiment = this.detectSentiment(args.text);
return {
sentiment: sentiment > 0 ? 'positive' : sentiment < 0 ? 'negative' : 'neutral',
score: sentiment,
confidence: Math.abs(sentiment)
};
}
private detectSentiment(text: string): number {
// Simple keyword-based sentiment analysis
const positiveWords = ['good', 'great', 'excellent', 'amazing', 'love'];
const negativeWords = ['bad', 'terrible', 'awful', 'horrible', 'hate'];
let score = 0;
const words = text.toLowerCase().split(/\s+/);
words.forEach(word => {
if (positiveWords.includes(word)) score += 0.3;
if (negativeWords.includes(word)) score -= 0.3;
});
return Math.max(-1, Math.min(1, score));
}
}
3. Run your server
npm start
Your MCP server starts on http://localhost:8080 with:
- HTTP endpoint:
http://localhost:8080/mcp - Health check:
http://localhost:8080/health
Installation & Packages
LeanMCP is modular. Start with the core packages, then add capabilities as needed.
Required for every MCP server
| Package | Purpose | Install |
|---|---|---|
| @leanmcp/cli | Project scaffolding and local dev / deploy workflow | npm install -g @leanmcp/cli or npx @leanmcp/cli |
| @leanmcp/core | MCP server runtime, decorators, schema validation | npm install @leanmcp/core |
Optional capability packages
| Package | Purpose | When to use | Install |
|---|---|---|---|
| @leanmcp/auth | Authentication and access control | Real users, permissions, multi-user MCP servers | npm install @leanmcp/auth |
| @leanmcp/elicitation | Structured user input during execution | Tools need guided or multi-step input | npm install @leanmcp/elicitation |
| @leanmcp/ui | UI components for MCP Apps | Interactive MCP experiences (advanced) | npm install @leanmcp/ui |
| @leanmcp/env-injection | Request-scoped environment / secret injection | Multi-tenant secrets, per-request config | npm install @leanmcp/env-injection |
| @leanmcp/utils | Shared utilities | Extending or building on LeanMCP internals | npm install @leanmcp/utils |
Global CLI Installation
npm install -g @leanmcp/cli
Project-Level Installation
npm install @leanmcp/core
npm install --save-dev @leanmcp/cli
Core Concepts (Click to expand)
Tools
Callable functions that perform actions (like API endpoints).
class AddInput {
@SchemaConstraint({ description: 'First number' })
a!: number;
@SchemaConstraint({ description: 'Second number' })
b!: number;
}
@Tool({
description: 'Calculate sum of two numbers',
inputClass: AddInput
})
async add(input: AddInput): Promise<{ result: number }> {
return { result: input.a + input.b };
}
// Tool name: "add" (from function name)
Prompts
Reusable prompt templates for LLM interactions.
@Prompt({ description: 'Generate a greeting prompt' })
greetingPrompt(args: { name?: string }) {
return {
messages: [{
role: 'user',
content: { type: 'text', text: `Say hello to ${args.name || 'there'}!` }
}]
};
}
// Prompt name: "greetingPrompt" (from function name)
Resources
Data endpoints that provide information (like REST GET endpoints).
@Resource({ description: 'Service statistics' })
getStats() {
return {
uptime: process.uptime(),
requestCount: 1523
};
}
// Resource URI: "servicename://getStats" (auto-generated)
Common Patterns (Click to expand)
Define a tool
@tool("search_docs")
async searchDocs(query: string) {
return await this.vectorStore.search(query)
}
Require authentication
@requireAuth()
@tool("get_user_data")
async getUserData() {
...
}
Ask for structured input
const input = await elicit({
type: "form",
fields: [...]
})
These snippets show common patterns only. Full API details live in the documentation.
Detailed Reference
CLI Commands (Click to expand)
The LeanMCP CLI provides an interactive experience for creating and managing MCP projects.
leanmcp create <project-name>
Creates a new MCP server project with interactive setup:
leanmcp create my-mcp-server
Interactive prompts:
- Auto-install dependencies (optional)
- Start dev server after creation (optional)
Generated structure:
my-mcp-server/
βββ main.ts # Entry point with HTTP server
βββ package.json # Project dependencies
βββ tsconfig.json # TypeScript configuration
βββ .gitignore # Git ignore rules
βββ .dockerignore # Docker ignore rules
βββ .env # Environment variables
βββ .env.local # Local overrides
βββ mcp/ # Services directory
βββ example/
βββ index.ts # Example service
leanmcp add <service-name>
Adds a new service to an existing project with auto-registration:
leanmcp add weather
What it does:
- Creates
mcp/weather/index.tswith boilerplate (Tool, Prompt, Resource examples) - Auto-registers the service in
main.ts - Ready to customize and use immediately
More CLI Features
For complete CLI documentation including all commands, options, and advanced usage, see @leanmcp/cli README.
Decorators (Click to expand)
Core Decorators
| Decorator | Purpose | Usage |
|---|---|---|
@Tool | Callable function | @Tool({ description?: string, inputClass?: Class }) |
@Prompt | Prompt template | @Prompt({ description?: string }) |
@Resource | Data endpoint | @Resource({ description?: string }) |
Schema Decorators
| Decorator | Purpose | Usage |
|---|---|---|
@Optional | Mark property as optional | Property decorator |
@SchemaConstraint | Add validation rules | Property decorator with constraints |
Available Constraints:
- String:
minLength,maxLength,pattern,enum,format,description,default - Number:
minimum,maximum,description,default - Array:
minItems,maxItems,description - Common:
description,default
Example:
class UserInput {
@SchemaConstraint({
description: 'User email address',
format: 'email'
})
email!: string;
@Optional()
@SchemaConstraint({
description: 'User age',
minimum: 18,
maximum: 120
})
age?: number;
@SchemaConstraint({
description: 'User roles',
enum: ['admin', 'user', 'guest'],
default: 'user'
})
role!: string;
}
Project Structure (Click to expand)
Main Entry Point (main.ts)
Simplified API (Recommended):
import { createHTTPServer } from "@leanmcp/core";
// Services are automatically discovered from ./mcp directory
await createHTTPServer({
name: "my-mcp-server",
version: "1.0.0",
port: 8080,
cors: true,
logging: true
});
Factory Pattern (Advanced):
import { createHTTPServer, MCPServer } from "@leanmcp/core";
import { ExampleService } from "./mcp/example/index.js";
const serverFactory = async () => {
const server = new MCPServer({
name: "my-mcp-server",
version: "1.0.0",
autoDiscover: false
});
server.registerService(new ExampleService());
return server.getServer();
};
await createHTTPServer(serverFactory, {
port: 8080,
cors: true
});
Service Structure (mcp/service-name/index.ts)
import { Tool, Prompt, Resource } from "@leanmcp/core";
class ToolInput {
@SchemaConstraint({ description: 'Input parameter' })
param!: string;
}
export class ServiceName {
@Tool({
description: 'Tool description',
inputClass: ToolInput
})
async toolMethod(args: ToolInput) {
// Tool implementation
return { result: 'success' };
}
@Prompt({ description: 'Prompt description' })
promptMethod(args: { param?: string }) {
// Prompt implementation
return {
messages: [{
role: 'user',
content: { type: 'text', text: 'Prompt text' }
}]
};
}
@Resource({ description: 'Resource description' })
resourceMethod() {
// Resource implementation
return { data: 'value' };
}
}
API Reference (Click to expand)
createHTTPServer(options | serverFactory, options?)
Creates and starts an HTTP server with MCP support.
Simplified API (Recommended):
await createHTTPServer({
name: string; // Server name (required)
version: string; // Server version (required)
port?: number; // Port number (default: 3001)
cors?: boolean | object; // Enable CORS (default: false)
logging?: boolean; // Enable logging (default: false)
debug?: boolean; // Enable debug logs (default: false)
autoDiscover?: boolean; // Auto-discover services (default: true)
mcpDir?: string; // Custom mcp directory path (optional)
sessionTimeout?: number; // Session timeout in ms (optional)
});
Example:
import { createHTTPServer } from "@leanmcp/core";
// Services automatically discovered from ./mcp directory
await createHTTPServer({
name: "my-mcp-server",
version: "1.0.0",
port: 3000,
cors: true,
logging: true
});
Factory Pattern (Advanced):
import { createHTTPServer, MCPServer } from "@leanmcp/core";
import { MyService } from "./mcp/myservice/index.js";
const serverFactory = async () => {
const server = new MCPServer({
name: "my-mcp-server",
version: "1.0.0",
autoDiscover: false
});
server.registerService(new MyService());
return server.getServer();
};
await createHTTPServer(serverFactory, {
port: 3000,
cors: true
});
MCPServer
Main server class for manual service registration.
Constructor Options:
const server = new MCPServer({
name: string; // Server name (required)
version: string; // Server version (required)
logging?: boolean; // Enable logging (default: false)
debug?: boolean; // Enable debug logs (default: false)
autoDiscover?: boolean; // Auto-discover services (default: true)
mcpDir?: string; // Custom mcp directory path (optional)
});
Methods:
registerService(instance)- Manually register a service instancegetServer()- Get the underlying MCP SDK server
Example:
import { MCPServer } from "@leanmcp/core";
const server = new MCPServer({
name: "my-server",
version: "1.0.0",
autoDiscover: false
});
server.registerService(new WeatherService());
server.registerService(new PaymentService());
Examples (Click to expand)
Complete Weather Service
import { Tool, Prompt, Resource, SchemaConstraint, Optional } from "@leanmcp/core";
class WeatherInput {
@SchemaConstraint({
description: 'City name',
minLength: 1
})
city!: string;
@Optional()
@SchemaConstraint({
description: 'Units',
enum: ['metric', 'imperial'],
default: 'metric'
})
units?: string;
}
class WeatherOutput {
@SchemaConstraint({ description: 'Temperature value' })
temperature!: number;
@SchemaConstraint({
description: 'Weather conditions',
enum: ['sunny', 'cloudy', 'rainy', 'snowy']
})
conditions!: string;
@SchemaConstraint({
description: 'Humidity percentage',
minimum: 0,
maximum: 100
})
humidity!: number;
}
export class WeatherService {
@Tool({
description: 'Get current weather for a city',
inputClass: WeatherInput
})
async getCurrentWeather(args: WeatherInput): Promise<WeatherOutput> {
// Simulate API call
return {
temperature: 72,
conditions: 'sunny',
humidity: 65
};
}
@Prompt({ description: 'Generate weather query prompt' })
weatherPrompt(args: { city?: string }) {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `What's the weather forecast for ${args.city || 'the city'}?`
}
}]
};
}
@Resource({ description: 'Supported cities list' })
getSupportedCities() {
return {
cities: ['New York', 'London', 'Tokyo', 'Paris', 'Sydney'],
count: 5
};
}
}
Calculator Service with Validation
import { Tool, SchemaConstraint } from "@leanmcp/core";
class CalculatorInput {
@SchemaConstraint({
description: 'First number',
minimum: -1000000,
maximum: 1000000
})
a!: number;
@SchemaConstraint({
description: 'Second number',
minimum: -1000000,
maximum: 1000000
})
b!: number;
}
class CalculatorOutput {
@SchemaConstraint({ description: 'Calculation result' })
result!: number;
}
export class CalculatorService {
@Tool({
description: 'Add two numbers',
inputClass: CalculatorInput
})
async add(args: CalculatorInput): Promise<CalculatorOutput> {
return { result: args.a + args.b };
}
@Tool({
description: 'Subtract two numbers',
inputClass: CalculatorInput
})
async subtract(args: CalculatorInput): Promise<CalculatorOutput> {
return { result: args.a - args.b };
}
@Tool({
description: 'Multiply two numbers',
inputClass: CalculatorInput
})
async multiply(args: CalculatorInput): Promise<CalculatorOutput> {
return { result: args.a * args.b };
}
@Tool({
description: 'Divide two numbers',
inputClass: CalculatorInput
})
async divide(args: CalculatorInput): Promise<CalculatorOutput> {
if (args.b === 0) {
throw new Error('Division by zero');
}
return { result: args.a / args.b };
}
}
Authenticated Service with AWS Cognito
import { Tool, SchemaConstraint } from "@leanmcp/core";
import { AuthProvider, Authenticated } from "@leanmcp/auth";
// Initialize auth provider
const authProvider = new AuthProvider('cognito', {
region: process.env.AWS_REGION,
userPoolId: process.env.COGNITO_USER_POOL_ID,
clientId: process.env.COGNITO_CLIENT_ID
});
await authProvider.init();
// Input class - no token field needed
class SendMessageInput {
@SchemaConstraint({
description: 'Channel to send message to',
minLength: 1
})
channel!: string;
@SchemaConstraint({
description: 'Message text',
minLength: 1
})
text!: string;
}
// Protect entire service with authentication
@Authenticated(authProvider)
export class SlackService {
@Tool({
description: 'Send message to Slack channel',
inputClass: SendMessageInput
})
async sendMessage(args: SendMessageInput) {
// Token automatically validated from _meta.authorization.token
// Only business arguments are passed here
return {
success: true,
channel: args.channel,
timestamp: Date.now().toString()
};
}
}
Client Usage:
// Call with authentication
await mcpClient.callTool({
name: "sendMessage",
arguments: {
channel: "#general",
text: "Hello world"
},
_meta: {
authorization: {
type: "bearer",
token: "your-jwt-token"
}
}
});
See examples/slack-with-auth for a complete working example.
E-commerce Product Search
A self-contained example with a mock product database (20 products, 5 categories).
What it demonstrates:
@Toolβ search with filters, pagination, sorting, product details, recommendations@Resourceβ full product catalog as a JSON data endpoint@Promptβ AI shopping assistant template
cd examples/ecommerce-search
npm install
npm start
# Dashboard: http://localhost:8080
# MCP endpoint: http://localhost:8080/mcp
See examples/ecommerce-search for full documentation and curl examples.
Development
Development (Click to expand)
Setting Up the Monorepo
# Clone the repository
git clone https://github.com/leanmcp/leanmcp-sdk.git
cd leanmcp-sdk
# Install dependencies
npm install
# Build all packages
npm run build
Monorepo Structure
leanmcp-sdk/
βββ package.json # Root workspace config
βββ tsconfig.base.json # Shared TypeScript config
βββ turbo.json # Turborepo configuration
βββ packages/
βββ cli/ # @leanmcp/cli - CLI binary
βββ core/ # @leanmcp/core - Core decorators & runtime
βββ auth/ # @leanmcp/auth - Authentication with @Authenticated decorator
βββ utils/ # @leanmcp/utils - Utilities (planned)
Building Individual Packages
# Build core package
cd packages/core
npm run build
# Build CLI package
cd packages/cli
npm run build
Testing Your Changes
# Create a test project
npx @leanmcp/cli create test-project
cd test-project
# Link local development version
npm link ../../packages/core
npm link ../../packages/cli
# Run the test project
npm start
Type Safety Benefits
- Compile-time validation - Catch errors before runtime
- Autocomplete - Full IntelliSense support in VS Code
- Refactoring - Safe renames and changes across your codebase
- No duplication - Define schemas once using TypeScript types
- Type inference - Automatic schema generation from decorators
Contributing
We Actively Welcome Contributors!
Contributing is Easy
New to open source? Perfect! We have plenty of good first issues waiting for you.
|
Fork & Contribute
|
Good First Issues
|
Join Community Chat with maintainers and contributors |
What You Can Contribute
- Documentation: Help make our guides clearer
- Examples: Add new service examples (weather, payments, etc.)
- Auth Integrations: Add support for new auth providers
- Bug Fixes: Fix reported issues
- Tests: Improve test coverage
- Features: Propose and implement new capabilities
See our Contributing Guide for detailed instructions.
Development Workflow
# Run tests
npm test
# Run linter
npm run lint
# Build all packages
npm run build
Business Collaboration: What Can We Do for You?
The LeanMCP founding team provides enterprise service support:
- MCP Agent deployment support (SDK + CLI + deployment environment configuration)
- Observability runtime platform (view agent call traces / usage behavior)
- Private deployment solutions (on-demand integration with your private cloud / intranet)
Contact us: founders@leanmcp.com
License
MIT License - see LICENSE file for details
Links
Acknowledgments
- Built on top of Model Context Protocol (MCP)
- Uses reflect-metadata for decorator support
- Inspired by the amazing MCP community
