> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leanmcp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quick Start

> Build your first MCP server in 5 minutes

# 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

```bash theme={null}
npm i -g @leanmcp/cli
```

Verify the installation:

```bash theme={null}
leanmcp --version
```

## Step 2: Create Your Project

```bash theme={null}
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:

```bash theme={null}
cd my-mcp-server
npm run dev
```

## Step 4: Test Your MCP

Use the MCP Inspector:

```bash theme={null}
npx @modelcontextprotocol/inspector http://localhost:3001/mcp
```

Or test with curl:

```bash theme={null}
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`:

```typescript theme={null}
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`:

```bash theme={null}
GEMINI_API_KEY=your-key-here
```

<Tip>
  Get a free Gemini API key at [aistudio.google.com/apikey](https://aistudio.google.com/apikey)
</Tip>

## Step 6: Build for Production

```bash theme={null}
npm run build
npm run start
```

Or set a custom port:

```bash theme={null}
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

<CardGroup cols={2}>
  <Card title="Tools" icon="wrench" href="/core-concepts/tools">
    Create more tools for AI to execute
  </Card>

  <Card title="Resources" icon="database" href="/core-concepts/resources">
    Expose data to AI agents
  </Card>

  <Card title="Prompts" icon="message" href="/core-concepts/prompts">
    Template prompts for AI
  </Card>

  <Card title="Auth" icon="lock" href="/building/auth">
    Secure your MCP server
  </Card>

  <Card title="Deployment" icon="rocket" href="/building/deployment">
    Deploy to production
  </Card>

  <Card title="CLI Reference" icon="terminal" href="/cli/installation">
    All CLI commands
  </Card>
</CardGroup>
