本文へ移動
cccskills
無料GitHub で公開

leanmcp-builder

Build MCP servers using the Leanmcp SDK with decorator-based TypeScript. Use this skill when users ask for "leanmcp", "MCP with decorators", "MCP with authentication", "MCP with elicitation", "MCP with environment injection", or want a simpler, more elegant way to build MCP servers. Leanmcp provides automatic schema generation, dependency injection, authentication, and user input collection.

インストール方法を見る

含まれるファイル(6)

  • SKILL.md12.0 KB
  • assets/main.ts353 B
  • assets/mcp-example-index.ts2.5 KB
  • assets/package.json651 B
  • assets/tsconfig.json436 B
  • references/decorators.md5.2 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Leanmcp Builder Skill

This skill guides you in building MCP servers using the Leanmcp SDK - a decorator-based TypeScript framework for elegant MCP development.

When to Use This Skill

  • User asks for "leanmcp server"
  • User wants "MCP with decorators"
  • User needs "authenticated MCP server"
  • User wants "simpler MCP development"
  • User mentions "@leanmcp/core"
  • User needs "user input collection" or "elicitation"
  • User wants "environment injection" for multi-tenant secrets

Leanmcp vs Vanilla MCP

FeatureVanilla MCPLeanmcp SDK
Tool definitionManual schema@Tool decorator with auto-schema
Input validationManualAutomatic with @SchemaConstraint
Service discoveryManual registrationAuto-discovery from ./mcp directory
AuthenticationDIY@Authenticated decorator
User inputNot built-in@Elicitation decorator
SecretsDIY@RequireEnv + getEnv()

Core Packages

# Minimal setup
npm install @leanmcp/core

# With authentication
npm install @leanmcp/auth

# With user input forms
npm install @leanmcp/elicitation

# With user secrets
npm install @leanmcp/env-injection

# CLI (global or dev)
npm install -g @leanmcp/cli

Project Structure

my-leanmcp-server/
├── main.ts                 # Entry point (minimal)
├── mcp/                    # Auto-discovered services
│   ├── example/
│   │   └── index.ts        # Exports ExampleService
│   └── myservice/
│       └── index.ts        # Exports MyService
├── package.json
├── tsconfig.json
└── .env

Required package.json

{
  "name": "my-leanmcp-server",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "leanmcp dev",
    "start": "leanmcp start",
    "build": "leanmcp build"
  },
  "dependencies": {
    "@leanmcp/core": "latest",
    "dotenv": "^16.5.0"
  },
  "devDependencies": {
    "@leanmcp/cli": "latest",
    "@types/node": "^20.0.0",
    "typescript": "^5.6.3"
  }
}

TypeScript Configuration

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["**/*.ts"]
}

Entry Point (main.ts)

import dotenv from 'dotenv';
import { createHTTPServer } from '@leanmcp/core';

dotenv.config();

// Services are automatically discovered from ./mcp directory
await createHTTPServer({
  name: 'my-leanmcp-server',
  version: '1.0.0',
  port: 3001,
  cors: true,
  logging: true,
});

@Tool Decorator

Marks a method as an MCP tool with automatic schema generation.

Basic Tool

import { Tool } from '@leanmcp/core';

export class MyService {
  @Tool({ description: 'Echo a message back' })
  async echo(args: { message: string }) {
    return { echoed: args.message };
  }
}

Tool with Input Class (Recommended)

import { Tool, SchemaConstraint, Optional } from '@leanmcp/core';

class CalculateInput {
  @SchemaConstraint({ description: 'First number', minimum: -1000000 })
  a!: number;

  @SchemaConstraint({ description: 'Second number', minimum: -1000000 })
  b!: number;

  @Optional()
  @SchemaConstraint({ 
    description: 'Operation to perform',
    enum: ['add', 'subtract', 'multiply', 'divide'],
    default: 'add'
  })
  operation?: string;
}

export class MathService {
  @Tool({ 
    description: 'Perform arithmetic operations',
    inputClass: CalculateInput
  })
  async calculate(input: CalculateInput) {
    const a = Number(input.a);
    const b = Number(input.b);
    
    switch (input.operation || 'add') {
      case 'add': return { result: a + b };
      case 'subtract': return { result: a - b };
      case 'multiply': return { result: a * b };
      case 'divide': 
        if (b === 0) throw new Error('Division by zero');
        return { result: a / b };
      default: throw new Error('Invalid operation');
    }
  }
}

Tool Naming

Tool name is derived from the method name:

  • async calculate(...) -> tool name: calculate
  • async sendMessage(...) -> tool name: sendMessage

@Resource Decorator

import { Resource } from '@leanmcp/core';

export class InfoService {
  @Resource({ description: 'Get server information' })
  async serverInfo() {
    return {
      contents: [{
        uri: 'server://info',
        mimeType: 'application/json',
        text: JSON.stringify({
          name: 'my-server',
          version: '1.0.0',
          uptime: process.uptime()
        })
      }]
    };
  }
}

@Prompt Decorator

import { Prompt } from '@leanmcp/core';

export class PromptService {
  @Prompt({ description: 'Generate a greeting prompt' })
  async greeting(args: { name?: string }) {
    return {
      messages: [{
        role: 'user' as const,
        content: {
          type: 'text' as const,
          text: `Hello ${args.name || 'there'}! Welcome to my server.`
        }
      }]
    };
  }
}

@Authenticated Decorator

Add authentication to your service:

import { Tool } from '@leanmcp/core';
import { Authenticated, AuthProvider, authUser } from '@leanmcp/auth';

const authProvider = new AuthProvider('cognito', {
  region: 'us-east-1',
  userPoolId: 'us-east-1_XXXXXXXXX',
  clientId: 'your-client-id'
});
await authProvider.init();

@Authenticated(authProvider)
export class SecureService {
  @Tool({ description: 'Get user data' })
  async getUserData() {
    // authUser is automatically available
    return { 
      userId: authUser.sub,
      email: authUser.email 
    };
  }
}

Supported providers: AWS Cognito, Clerk, Auth0, Leanmcp

@Elicitation Decorator

Collect structured user input:

import { Tool } from '@leanmcp/core';
import { Elicitation } from '@leanmcp/elicitation';

export class ChannelService {
  @Tool({ description: 'Create a new channel' })
  @Elicitation({
    title: 'Channel Details',
    fields: [
      { name: 'channelName', label: 'Channel Name', type: 'text', required: true },
      { name: 'isPrivate', label: 'Private Channel', type: 'boolean', defaultValue: false },
      { name: 'topic', label: 'Topic', type: 'textarea' }
    ]
  })
  async createChannel(args: { channelName: string; isPrivate: boolean; topic?: string }) {
    return { success: true, channel: args.channelName };
  }
}

Field types: text, textarea, number, boolean, select, multiselect, email, url, date

@RequireEnv Decorator

Inject user-specific secrets:

import { Tool } from '@leanmcp/core';
import { Authenticated } from '@leanmcp/auth';
import { RequireEnv, getEnv } from '@leanmcp/env-injection';

@Authenticated(authProvider, { projectId: 'my-project' })
export class SlackService {
  @Tool({ description: 'Send Slack message' })
  @RequireEnv(['SLACK_TOKEN', 'SLACK_CHANNEL'])
  async sendMessage(args: { message: string }) {
    const token = getEnv('SLACK_TOKEN')!;
    const channel = getEnv('SLACK_CHANNEL')!;
    
    await fetch('https://slack.com/api/chat.postMessage', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ channel, text: args.message })
    });
    
    return { success: true };
  }
}

Complete Service Example

// mcp/products/index.ts
import { Tool, Resource, SchemaConstraint, Optional } from '@leanmcp/core';

class CreateProductInput {
  @SchemaConstraint({ description: 'Product name', minLength: 1 })
  name!: string;

  @SchemaConstraint({ description: 'Price in dollars', minimum: 0 })
  price!: number;

  @Optional()
  @SchemaConstraint({ description: 'Product description' })
  description?: string;
}

class UpdateProductInput {
  @SchemaConstraint({ description: 'Product ID' })
  id!: string;

  @Optional()
  @SchemaConstraint({ description: 'Product name' })
  name?: string;

  @Optional()
  @SchemaConstraint({ description: 'Price in dollars', minimum: 0 })
  price?: number;
}

export class ProductsService {
  private products: Map<string, any> = new Map();

  @Tool({ description: 'Create a new product', inputClass: CreateProductInput })
  async createProduct(input: CreateProductInput) {
    const id = crypto.randomUUID();
    const product = { id, ...input, createdAt: new Date().toISOString() };
    this.products.set(id, product);
    return { success: true, product };
  }

  @Tool({ description: 'List all products' })
  async listProducts() {
    return { products: Array.from(this.products.values()) };
  }

  @Tool({ description: 'Update a product', inputClass: UpdateProductInput })
  async updateProduct(input: UpdateProductInput) {
    const product = this.products.get(input.id);
    if (!product) throw new Error('Product not found');
    
    if (input.name) product.name = input.name;
    if (input.price) product.price = input.price;
    product.updatedAt = new Date().toISOString();
    
    return { success: true, product };
  }

  @Tool({ description: 'Delete a product' })
  async deleteProduct(args: { id: string }) {
    if (!this.products.has(args.id)) throw new Error('Product not found');
    this.products.delete(args.id);
    return { success: true };
  }

  @Resource({ description: 'Get product statistics' })
  async productStats() {
    const products = Array.from(this.products.values());
    return {
      contents: [{
        uri: 'products://stats',
        mimeType: 'application/json',
        text: JSON.stringify({
          total: products.length,
          totalValue: products.reduce((sum, p) => sum + p.price, 0)
        })
      }]
    };
  }
}

Server Configuration Options

import { MCPServer, createHTTPServer } from '@leanmcp/core';

// Simple API (recommended)
await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
  port: 3001,
  cors: true,
  logging: true
});

// Advanced: Factory pattern with manual registration
const serverFactory = async () => {
  const server = new MCPServer({
    name: 'my-server',
    version: '1.0.0',
    autoDiscover: false,  // Disable auto-discovery
    logging: true
  });
  
  server.registerService(new MyService());
  return server.getServer();
};

await createHTTPServer(serverFactory, {
  port: 3001,
  cors: true
});

Editing Guidelines

DO

  • Add new services in mcp/servicename/index.ts
  • Use @Tool, @Resource, @Prompt decorators
  • Use @SchemaConstraint for input validation
  • Use @Optional() for optional fields
  • Export service classes from index.ts
  • Follow decorator patterns consistently

DON'T

  • Wrap services in AppProvider (CLI handles this)
  • Use direct imports for components in @UIApp
  • Completely rewrite existing files
  • Add terminal/command instructions
  • Remove existing working code

Error Handling

@Tool({ description: 'Divide two numbers' })
async divide(input: { a: number; b: number }) {
  if (input.b === 0) {
    throw new Error('Division by zero is not allowed');
  }
  return { result: input.a / input.b };
}

Errors are automatically caught and formatted as MCP error responses.

Best Practices

  1. Always provide descriptions - Helps LLMs understand tool purpose
  2. Use inputClass for complex inputs - Automatic schema generation
  3. Return structured data - Objects with clear field names
  4. Handle errors gracefully - Throw descriptive errors
  5. Keep tools focused - One tool = one clear action
  6. Organize by domain - One service per business domain
  7. Use schema constraints - Validate inputs with min/max/enum

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Analyze tau2-bench task export files to identify exactly where and why tasks failed. Map failures to specific conversation rounds, show root causes, check whether relevant documentation was available, and cite sources. Use this skill whenever you need to debug a tau2-bench evaluation, understand why an agent made wrong decisions, identify missing knowledge, or review task performance—especially when context length, eligibility criteria, or policy compliance might be at fault.

日本語の概要は準備中です。原文の説明を表示しています。

Leanmcp/gateway-skills2,1112026年10月8日 更新

How to develop, modify, and extend the Claude Code codebase following its established programming style, patterns, and conventions. Use this skill whenever working on the claude-code source — adding tools, commands, utils, components, types, hooks, services, or fixing bugs. Also use when the user asks about claude-code architecture, coding standards, file organization, or wants to understand how the codebase works. This skill ensures all contributions match the existing codebase style exactly.

日本語の概要は準備中です。原文の説明を表示しています。

Leanmcp/gateway-skills2,1112026年10月8日 更新

Generate a complete, beginner-friendly explanation of an entire code repository as a set of markdown files. Maps the repo structure with `treecap` (installing it if missing), studies the actual source, then writes a `CODE_EXPLANATION/` folder containing an `OVERVIEW.md` plus numbered deep-dive files (01_architecture.md, 02_models.md, ...) that explain how the code is built, what it does, and how the pieces fit together — illustrated with real code excerpts. Use this skill whenever the user wants to understand, document, onboard onto, or get a walkthrough of a codebase: phrases like "explain this repo", "explain the code", "code explanation", "walk me through this codebase", "help me understand how this is built", "document the architecture", or "what does this project do" — even if they don't say the word "skill". Prefer this over ad-hoc summaries whenever the goal is a durable, written explanation of a whole repo.

日本語の概要は準備中です。原文の説明を表示しています。

Leanmcp/gateway-skills2,1112026年10月8日 更新

Run inference on Fireworks AI — direct, through the Leanmcp AI Gateway proxy (aigateway.leanmcp.com) for observability, or via LiteLLM / the OpenAI SDK / raw HTTP. Covers the endpoint-and-key matrix for each route, serverless vs dedicated deployments and their cold starts, browsing and filtering the model catalogue, reasoning models that return content:null with the answer in reasoning_content, function calling, concurrency and load testing, and runner scripts where many models share one protocol with a --use-proxy toggle. Use this skill whenever the user mentions Fireworks, FIREWORKS_API_KEY, api.fireworks.ai, fireworks_ai/ model ids, accounts/fireworks/models/..., a Fireworks deployment id, the Leanmcp gateway or LEANMCP_API_KEY, app.leanmcp.com observability, or gpt-oss / kimi / glm / qwen / deepseek served on Fireworks; wants to route LLM calls through a proxy for logging or cost tracking; is debugging empty responses, 404s, scope errors, cold starts, or "the gateway isn't working"; wants to pick a Fireworks model with tool calling; or wants to sweep several models over one benchmark. Reach for it even on vague asks like "why is this model returning nothing" or "set up inference for these models" when Fireworks is in play.

日本語の概要は準備中です。原文の説明を表示しています。

Leanmcp/gateway-skills2,1112026年10月8日 更新

Build, run, debug and ship 3D city games in Godot 4 — procedurally generated cities (block grids, districts, weighted building typologies, MultiMesh batching, facade shaders, terrain, day/night, landmarks), first-person controllers, headless verification, Blender-generated .glb assets, rigged character and animation packs, real OpenStreetMap-derived cities, WebAssembly export, and cloud GPU streaming on GCP. Use this skill whenever the work involves Godot or a .gd/.tscn/.gdshader/project.godot file; whenever someone wants to start, set up, clone onto a new machine, or stand up a 3D game or city generator; whenever they mention procedural city, city generator, block grid, building typology, MultiMesh, facade shader, terrain heightmap, navmesh, Godot export templates, export presets, headless Godot, .glb pipeline, Blender asset generation, character rigs or borrowed animations; and whenever they want to deploy or share a game — web export, SharedArrayBuffer/COOP/COEP, Selkies, pixel streaming, GPU VM, or "let people play this without installing anything." Reach for it even on vague asks like "get this running", "why is my city slow", "my model faces backwards", "make it work on a cloud machine", or "set this up somewhere else too" when a Godot 3D project is in play.

日本語の概要は準備中です。原文の説明を表示しています。

Leanmcp/gateway-skills2,1112026年10月8日 更新

Scaffold a native iOS app (SwiftUI, no dependencies) from nothing, run it in the Simulator, and archive + upload it to TestFlight entirely from the command line. Bundles working scripts for icon generation, simulator run, and signed upload via the App Store Connect API. Use this whenever the user wants to build, run, ship, or debug an iOS/iPhone/iPad app, mentions Xcode, SwiftUI, .xcodeproj, xcodebuild, simctl, provisioning, code signing, App Store Connect, TestFlight, archiving, or "get this on my phone" — and also when an existing iOS build is failing on icons, signing, teams, destinations, or duplicate build numbers.

日本語の概要は準備中です。原文の説明を表示しています。

Leanmcp/gateway-skills2,1112026年10月8日 更新

Leanmcp のスキルをすべて見る

このスキルの問題を報告する