Gemini MCP Server

  • src
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { GoogleGenerativeAI, GenerativeModel } from '@google/generative-ai'; import { config } from 'dotenv'; import { z } from "zod"; // Immediately send the startup message before anything else can write to stdout process.stdout.write(JSON.stringify({ jsonrpc: "2.0", method: "startup", params: { transport: "stdio" } }) + '\n'); // Redirect stdout to stderr for everything else const originalStdoutWrite = process.stdout.write.bind(process.stdout); process.stdout.write = (chunk: any, ...args: any[]) => { return process.stderr.write(chunk, ...args); }; // Redirect console methods to stderr const consoleMethods = ['log', 'info', 'warn', 'error', 'debug'] as const; consoleMethods.forEach(method => { (console as any)[method] = (...args: any[]) => process.stderr.write(`[${method}] ` + args.join(' ') + '\n'); }); // Suppress npm and Node.js startup messages process.env.NODE_ENV = 'production'; process.env.NO_UPDATE_NOTIFIER = '1'; process.env.SUPPRESS_NO_CONFIG_WARNING = 'true'; process.env.npm_config_loglevel = 'silent'; // Load environment variables config(); const GEMINI_API_KEY = process.env.GEMINI_API_KEY ?? ''; if (!GEMINI_API_KEY) { console.error('GEMINI_API_KEY environment variable is required'); process.exit(1); } // Define tool schemas const generateTextSchema = z.object({ prompt: z.string().min(1), temperature: z.number().min(0).max(1).optional(), maxOutputTokens: z.number().min(1).max(8192).optional(), topK: z.number().min(1).max(40).optional(), topP: z.number().min(0).max(1).optional(), stream: z.boolean().optional(), }); type GenerateTextParams = z.infer<typeof generateTextSchema>; class GeminiMCPServer { private model: GenerativeModel; private server: McpServer; private transport: StdioServerTransport; private chat: any; // Store chat session constructor() { const genAI = new GoogleGenerativeAI(GEMINI_API_KEY); this.model = genAI.getGenerativeModel({ model: 'gemini-1.5-pro' }); this.chat = this.model.startChat(); this.server = new McpServer({ name: "gemini", version: "1.0.0", capabilities: { tools: { generate_text: { description: "Generate text using Gemini Pro model", streaming: true } } } }); this.transport = new StdioServerTransport(); } private async generateText(params: GenerateTextParams) { try { const { prompt, temperature = 0.7, maxOutputTokens = 8192, topK, topP, stream = false } = params; const generationConfig = { temperature, maxOutputTokens, topK, topP, }; console.log('Sending message to Gemini:', prompt); if (stream) { const result = await this.chat.sendMessageStream(prompt); let fullText = ''; for await (const chunk of result.stream) { const chunkText = chunk.text(); fullText += chunkText; // Send intermediate chunk as progress event // Note: Progress events are not yet supported in MCP SDK // this.server.emit("generate_text/progress", { // content: [{ // type: "text", // text: chunkText // }] // }); } console.log('Received streamed response from Gemini:', fullText); return { content: [{ type: "text" as const, text: fullText }] }; } else { const result = await this.chat.sendMessage(prompt); const response = result.response.text(); console.log('Received response from Gemini:', response); return { content: [{ type: "text" as const, text: response }] }; } } catch (err) { console.error('Error generating content:', err); return { content: [{ type: "text" as const, text: err instanceof Error ? err.message : 'Internal error' }], isError: true }; } } async start() { try { console.info('Initializing Gemini MCP server...'); // Register generate_text tool this.server.tool( "generate_text", generateTextSchema.shape, async (args: GenerateTextParams) => this.generateText(args) ); // Restore stdout for MCP communication process.stdout.write = originalStdoutWrite; // Connect using stdio transport await this.server.connect(this.transport); console.info('Server started successfully and waiting for messages...'); } catch (error) { console.error('Failed to start server:', error); process.exit(1); } } async stop() { try { // Note: Disconnect is not yet supported in MCP SDK // await this.server.disconnect(); console.info('Server stopped successfully'); } catch (error) { console.error('Error stopping server:', error); process.exit(1); } } } // Handle shutdown gracefully process.on('SIGINT', () => { console.info('Server shutting down'); process.exit(0); }); // Handle uncaught errors process.on('uncaughtException', (error) => { console.error('Uncaught exception:', error); process.exit(1); }); process.on('unhandledRejection', (reason) => { console.error('Unhandled rejection:', reason); process.exit(1); }); // Start the server const server = new GeminiMCPServer(); server.start().catch(error => { console.error('Unhandled error:', error); process.exit(1); });