Skip to main content

Quick Start

In this guide, you’ll install the LeanMCP CLI, create a new project, and build a real image generation service using Gemini’s Nano Banana API — complete with tools, resources, and schema validation that AI agents can discover and use.

Prerequisites

  • Node.js >= 18
  • npm >= 9

Step 1: Install the CLI

npm i -g @leanmcp/cli
Verify the installation:
leanmcp --version

Step 2: Create Your Project

leanmcp create my-mcp-server
The CLI will ask:
  • Install dependencies? → Yes
  • Start dev server? → Yes
Project structure:
my-mcp-server/
├── main.ts              # Entry point
├── package.json
├── tsconfig.json
└── mcp/
    └── example/
        └── index.ts     # Example service

Step 3: Server is Running

After creation, the server starts automatically:
Server running on http://localhost:3001
MCP endpoint: http://localhost:3001/mcp
Health check: http://localhost:3001/health
To restart later:
cd my-mcp-server
npm run dev

Step 4: Test Your MCP

Use the MCP Inspector:
npx @modelcontextprotocol/inspector http://localhost:3001/mcp
Or test with curl:
curl http://localhost:3001/mcp \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": { "name": "test", "version": "1.0.0" }
    }
  }'

Step 5: Add Gemini Image Generation

Let’s build a real example: image generation with Gemini (Nano Banana). Create mcp/gemini/index.ts:
import { Tool, Resource, SchemaConstraint, Optional } from "@leanmcp/core";
import fs from "fs";
import path from "path";

// --- Input Schema ---

class GenerateImageInput {
  @SchemaConstraint({ 
    description: "Text description of the image to generate",
    minLength: 1
  })
  prompt!: string;

  @Optional()
  @SchemaConstraint({ 
    description: "Model: nano-banana (fast) or nano-banana-pro (advanced)",
    enum: ["nano-banana", "nano-banana-pro"],
    default: "nano-banana"
  })
  model?: "nano-banana" | "nano-banana-pro";

  @Optional()
  @SchemaConstraint({ 
    description: "Aspect ratio",
    enum: ["1:1", "16:9", "9:16", "4:3"],
    default: "1:1"
  })
  aspectRatio?: string;
}

// --- Service ---

export class GeminiImageService {
  private apiKey = process.env.GEMINI_API_KEY || "";
  private baseUrl = "https://generativelanguage.googleapis.com/v1beta/models";
  private outputDir = path.join(process.cwd(), "generated-images");

  private modelMap = {
    "nano-banana": "gemini-2.5-flash-image",
    "nano-banana-pro": "gemini-3-pro-image-preview"
  };

  constructor() {
    if (!fs.existsSync(this.outputDir)) {
      fs.mkdirSync(this.outputDir, { recursive: true });
    }
  }

  @Tool({ 
    description: "Generate an image from text using Gemini (Nano Banana)",
    inputClass: GenerateImageInput 
  })
  async generateImage(input: GenerateImageInput) {
    const modelKey = input.model || "nano-banana";
    const modelName = this.modelMap[modelKey];

    const response = await fetch(
      `${this.baseUrl}/${modelName}:generateContent?key=${this.apiKey}`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          contents: [{ parts: [{ text: input.prompt }] }],
          generationConfig: {
            responseModalities: ["IMAGE"],
            imageConfig: { aspectRatio: input.aspectRatio || "1:1" }
          }
        })
      }
    );

    const result = await response.json();
    const imageData = result.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data;

    // Save to disk
    const filename = `gemini_${Date.now()}.png`;
    const filepath = path.join(this.outputDir, filename);
    fs.writeFileSync(filepath, Buffer.from(imageData, "base64"));

    return { success: true, savedTo: filepath, filename };
  }

  @Resource({ description: "Available Gemini models", mimeType: "application/json" })
  getModels() {
    return {
      contents: [{
        uri: "gemini://models",
        mimeType: "application/json",
        text: JSON.stringify({
          "nano-banana": "Fast, 1K resolution",
          "nano-banana-pro": "Advanced, up to 4K"
        })
      }]
    };
  }
}
Add your API key to .env:
GEMINI_API_KEY=your-key-here
Get a free Gemini API key at aistudio.google.com/apikey

Step 6: Build for Production

npm run build
npm run start
Or set a custom port:
PORT=4000 npm run start

What You Built

You now have a working MCP server with:
  • Tool: generateImage - Generate images from text prompts
  • Resource: getModels - Lists available Gemini models
  • Auto-discovery - Services in mcp/ are automatically registered

Next Steps

Tools

Create more tools for AI to execute

Resources

Expose data to AI agents

Prompts

Template prompts for AI

Auth

Secure your MCP server

Deployment

Deploy to production

CLI Reference

All CLI commands