Skip to main content
Glama
index.js11.6 kB
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { GoogleGenerativeAI } from "@google/generative-ai"; import { writeFile, mkdir } from "fs/promises"; import { join, dirname } from "path"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Gemini API 초기화 const apiKey = process.env.GEMINI_API_KEY; if (!apiKey) { console.error("Error: GEMINI_API_KEY environment variable is required"); process.exit(1); } const genAI = new GoogleGenerativeAI(apiKey); // MCP 서버 생성 const server = new Server( { name: "gemini-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // 사용 가능한 도구 목록 server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "ask_gemini", description: "Use Gemini for large context analysis (1M tokens), architecture design, or whole codebase review. Best for tasks requiring understanding of entire projects.", inputSchema: { type: "object", properties: { prompt: { type: "string", description: "The question or task for Gemini", }, context: { type: "string", description: "Optional: Large codebase, multiple files, or extensive context to analyze", }, model: { type: "string", description: "Model to use: 'flash' (default, free, fast) or 'pro' (3 Pro, latest model, better quality, paid)", enum: ["flash", "pro"], default: "flash", }, }, required: ["prompt"], }, }, { name: "gemini_analyze_codebase", description: "Specialized tool for analyzing entire codebases. Gemini will find patterns, duplications, architectural issues, and suggest improvements.", inputSchema: { type: "object", properties: { codebase: { type: "string", description: "The entire codebase or multiple files concatenated", }, focus: { type: "string", description: "What to focus on: 'architecture', 'duplications', 'security', 'performance', or 'general'", enum: [ "architecture", "duplications", "security", "performance", "general", ], }, }, required: ["codebase"], }, }, { name: "generate_image_gemini", description: "Generate images using Gemini 2.5 Flash Image (Nano Banana). Best for contextual understanding, image editing, multi-image composition, and iterative refinement. Free tier available.", inputSchema: { type: "object", properties: { prompt: { type: "string", description: "Description of the image to generate (in English, max 480 tokens)", }, numberOfImages: { type: "number", description: "Number of images to generate (1-4, default: 1)", default: 1, minimum: 1, maximum: 4, }, }, required: ["prompt"], }, }, { name: "generate_image_imagen", description: "Generate images using Imagen 4. Best for photorealistic quality, high-resolution outputs, and professional branding. Paid service.", inputSchema: { type: "object", properties: { prompt: { type: "string", description: "Description of the image to generate (in English, max 480 tokens)", }, numberOfImages: { type: "number", description: "Number of images to generate (1-4, default: 1)", default: 1, minimum: 1, maximum: 4, }, }, required: ["prompt"], }, }, ], }; }); // 도구 실행 핸들러 server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { if (name === "ask_gemini") { const { prompt, context, model = "flash" } = args; // 모델 선택 const modelName = model === "pro" ? "gemini-3-pro-preview" // Gemini 3 Pro (최신 모델, 2025년 11월 출시) : "gemini-2.5-flash"; // 2.5 Flash (무료) const geminiModel = genAI.getGenerativeModel({ model: modelName }); // 프롬프트 구성 const fullPrompt = context ? `Context/Codebase:\n\`\`\`\n${context}\n\`\`\`\n\nTask: ${prompt}` : prompt; // Gemini 호출 const result = await geminiModel.generateContent(fullPrompt); const response = await result.response; const text = response.text(); return { content: [ { type: "text", text: `[Gemini ${ model === "pro" ? "3.0 Pro" : "2.5 Flash" }]\n\n${text}`, }, ], }; } if (name === "gemini_analyze_codebase") { const { codebase, focus = "general" } = args; const focusPrompts = { architecture: "Analyze the overall architecture, design patterns, and structural issues. Suggest improvements.", duplications: "Find duplicated code, similar patterns, and opportunities for refactoring. Be specific.", security: "Identify security vulnerabilities, unsafe practices, and potential exploits.", performance: "Find performance bottlenecks, inefficient algorithms, and optimization opportunities.", general: "Provide a comprehensive analysis covering architecture, code quality, potential issues, and improvements.", }; const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" }); const prompt = `You are an expert code reviewer. Analyze this codebase with focus on: ${focus} ${focusPrompts[focus]} Codebase: \`\`\` ${codebase} \`\`\` Provide: 1. Key findings 2. Specific issues with file/line references 3. Actionable recommendations 4. Priority ranking (High/Medium/Low)`; const result = await model.generateContent(prompt); const response = await result.response; return { content: [ { type: "text", text: `[Gemini Codebase Analysis - ${focus}]\n\n${response.text()}`, }, ], }; } if (name === "generate_image_gemini") { const { prompt, numberOfImages = 1 } = args; const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash-image", }); const result = await model.generateContent({ contents: [{ role: "user", parts: [{ text: prompt }] }], generationConfig: { responseModalities: ["image"], }, }); const response = await result.response; const images = response.candidates?.[0]?.content?.parts?.filter( (part) => part.inlineData ); if (!images || images.length === 0) { throw new Error("No images were generated"); } // 이미지 저장 const outputDir = join(__dirname, "generated_images"); await mkdir(outputDir, { recursive: true }); const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); const savedPaths = []; for (let i = 0; i < images.length; i++) { const img = images[i]; const ext = img.inlineData.mimeType.split("/")[1] || "png"; const filename = `gemini_${timestamp}_${i + 1}.${ext}`; const filepath = join(outputDir, filename); const buffer = Buffer.from(img.inlineData.data, "base64"); await writeFile(filepath, buffer); savedPaths.push(filepath); } return { content: [ { type: "text", text: `[Gemini 2.5 Flash Image (Nano Banana)]\n\nGenerated ${images.length} image(s) for: "${prompt}"\n\nSaved to:\n${savedPaths.map(p => `- ${p}`).join("\n")}`, }, ...images.map((img) => ({ type: "image", data: img.inlineData.data, mimeType: img.inlineData.mimeType, })), ], }; } if (name === "generate_image_imagen") { const { prompt, numberOfImages = 1 } = args; // REST API를 사용하여 Imagen 4 호출 const response = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/imagen-4.0-generate-001:predict`, { method: "POST", headers: { "x-goog-api-key": apiKey, "Content-Type": "application/json", }, body: JSON.stringify({ instances: [{ prompt: prompt }], parameters: { sampleCount: numberOfImages, }, }), } ); if (!response.ok) { const errorText = await response.text(); throw new Error(`Imagen API error: ${response.status} - ${errorText}`); } const result = await response.json(); // predictions 배열 확인 (실제 API 응답 구조) if (!result.predictions || result.predictions.length === 0) { throw new Error(`No images were generated. Response: ${JSON.stringify(result)}`); } // 이미지 저장 const outputDir = join(__dirname, "generated_images"); await mkdir(outputDir, { recursive: true }); const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); const savedPaths = []; for (let i = 0; i < result.predictions.length; i++) { const img = result.predictions[i]; const filename = `imagen_${timestamp}_${i + 1}.png`; const filepath = join(outputDir, filename); // bytesBase64Encoded 필드 사용 const buffer = Buffer.from(img.bytesBase64Encoded, "base64"); await writeFile(filepath, buffer); savedPaths.push(filepath); } return { content: [ { type: "text", text: `[Imagen 4]\n\nGenerated ${result.predictions.length} image(s) for: "${prompt}"\n\nSaved to:\n${savedPaths.map(p => `- ${p}`).join("\n")}\n\nNote: All images include SynthID watermark for authenticity.`, }, ...result.predictions.map((img) => ({ type: "image", data: img.bytesBase64Encoded, mimeType: img.mimeType || "image/png", })), ], }; } throw new Error(`Unknown tool: ${name}`); } catch (error) { return { content: [ { type: "text", text: `Error calling Gemini: ${error.message}`, }, ], isError: true, }; } }); // 서버 시작 async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Gemini MCP server running"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Yoon-jongho/claude-to-gemini'

If you have feedback or need assistance with the MCP directory API, please join our Discord server