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