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"
})
}]
};
}
}