Gemini Pro Mcp

Gemini 2.5 Pro MCP: How to Use MCP Servers with Gemini 2.5 Pro

Introduction

Multi-Cloud Protocol (MCP) servers represent a paradigm shift in AI system architecture, establishing a standardized communication framework between large language models (LLMs) and external services. When implemented with Google's Gemini 2.5 Pro, MCP servers create a bidirectional communication channel that significantly enhances AI capabilities through real-time external data access, API integration, and autonomous operation. This architecture transforms the traditionally isolated LLM into an extensible system capable of complex, multi-step workflows with persistent state management.

Gemini 2.5 Pro's implementation of MCP enables the model to initiate requests to external services, process structured responses, and maintain coherent multi-turn interactions, all within a standardized protocol layer. This capability transcends traditional API-based integrations by implementing a stateful, event-driven architecture that persists contextual information across complex automation sequences.

Explore the comprehensive implementation in the official repository: github.com/GuiBibeau/mcp-gemini-tutorial (opens in a new tab)

MCP Technical Architecture

The MCP architecture implements a client-server model with standardized JSON-based message formats to facilitate bidirectional communication between Gemini 2.5 Pro and external services. This architecture consists of several key components:

Protocol Specification

The MCP protocol defines a structured message format that enables standardized communication:

{
  "message_id": "msg_123456789",
  "timestamp": "2025-04-04T15:04:27.084Z",
  "source": "client",
  "destination": "server",
  "message_type": "request",
  "content": {
    "action": "search",
    "parameters": {
      "query": "quantum computing applications",
      "limit": 5
    }
  },
  "metadata": {
    "session_id": "sess_abcdef123456",
    "request_id": "req_789012345"
  }
}

Server Components

An MCP server implementation comprises several key modules:

  1. Request Handler: Processes incoming requests from Gemini 2.5 Pro, validates message format, and routes to appropriate service adapters
  2. Service Adapters: Translates standardized MCP protocol messages into service-specific API calls
  3. Response Formatter: Converts external API responses into standardized MCP message format
  4. Session Manager: Maintains stateful interactions across multiple requests
  5. Authentication Module: Manages OAuth flows and API key authentication

Client Implementation

The client side, implemented within Gemini 2.5 Pro's context, requires:

  1. MCP Protocol Parser: Interprets and generates protocol-compliant messages
  2. Request Formatter: Constructs well-formed MCP request objects
  3. Response Handler: Processes MCP response messages and extracts relevant data
  4. Error Handler: Manages error states and implements retry mechanisms

Implementation Process

Server Setup

Setting up an MCP server for Gemini 2.5 Pro requires several technical steps:

  1. Initialize the project structure:
mkdir gemini-mcp-server
cd gemini-mcp-server
npm init -y
npm install express cors winston axios dotenv
  1. Configure environment variables:
# .env
PORT=3000
BRAVE_SEARCH_API_KEY=your_brave_search_api_key
GEMINI_API_KEY=your_gemini_api_key
LOG_LEVEL=info
  1. Implement the core server:
// server.js
const express = require('express');
const cors = require('cors');
const winston = require('winston');
const { handleRequest } = require('./handlers/requestHandler');
 
// Configure logging
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      )
    }),
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});
 
const app = express();
app.use(cors());
app.use(express.json());
 
// MCP endpoint
app.post('/mcp', async (req, res) => {
  try {
    const response = await handleRequest(req.body, logger);
    res.json(response);
  } catch (error) {
    logger.error(`Error processing MCP request: ${error.message}`);
    res.status(500).json({
      message_id: `error_${Date.now()}`,
      timestamp: new Date().toISOString(),
      message_type: 'error',
      content: {
        error: error.message
      }
    });
  }
});
 
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  logger.info(`MCP Server running on port ${PORT}`);
});
  1. Implement service-specific adapters:
// services/braveSearch.js
const axios = require('axios');
 
async function searchBrave(query, limit = 5) {
  const response = await axios.get('https://api.search.brave.com/res/v1/web/search', {
    headers: {
      'Accept': 'application/json',
      'Accept-Encoding': 'gzip',
      'X-Subscription-Token': process.env.BRAVE_SEARCH_API_KEY
    },
    params: {
      q: query,
      count: limit
    }
  });
 
  return {
    results: response.data.web.results.map(result => ({
      title: result.title,
      url: result.url,
      description: result.description
    })),
    total_results: response.data.web.totalResults
  };
}
 
module.exports = { searchBrave };

Client Configuration

Configuring Gemini 2.5 Pro to interact with the MCP server requires specific prompt engineering:

I'm going to help you use an MCP server to perform actions. When you need to search the web or call external APIs, please send me a message in the following JSON format:

{
  "action": "search",
  "parameters": {
    "query": "your search query",
    "limit": 5
  }
}

I'll send this to the MCP server and provide you with the results. Please wait for my response before continuing.

Integration Example

The following TypeScript implementation demonstrates a complete MCP client for Gemini 2.5 Pro:

// gemini-mcp-client.ts
import { GoogleGenerativeAI } from '@google/generative-ai';
import axios from 'axios';
 
interface MCPRequest {
  action: string;
  parameters: Record<string, any>;
}
 
interface MCPResponse {
  message_id: string;
  timestamp: string;
  message_type: string;
  content: any;
}
 
class GeminiMCPClient {
  private genAI: GoogleGenerativeAI;
  private model: any;
  private mcpServerUrl: string;
  private chat: any;
 
  constructor(geminiApiKey: string, mcpServerUrl: string) {
    this.genAI = new GoogleGenerativeAI(geminiApiKey);
    this.model = this.genAI.getGenerativeModel({ model: 'gemini-2.5-pro' });
    this.mcpServerUrl = mcpServerUrl;
    this.chat = this.model.startChat({
      history: [
        {
          role: 'user',
          parts: [
            {
              text: `You're an AI assistant capable of using external tools through MCP. 
                     When you need to search for information, respond with a JSON object:
                     {"action": "search", "parameters": {"query": "search query", "limit": 5}}`
            }
          ]
        },
        {
          role: 'model',
          parts: [
            {
              text: 'I understand. I'll use the MCP protocol to search for information when needed. When I need to search, I'll respond with the JSON format you specified.'
            }
          ]
        }
      ],
      generationConfig: {
        temperature: 0.2,
        topK: 40,
        topP: 0.95,
        maxOutputTokens: 8192,
      }
    });
  }
 
  private async processMCPRequest(request: MCPRequest): Promise<MCPResponse> {
    try {
      const response = await axios.post(this.mcpServerUrl, {
        message_id: `req_${Date.now()}`,
        timestamp: new Date().toISOString(),
        source: 'client',
        destination: 'server',
        message_type: 'request',
        content: request
      });
      return response.data;
    } catch (error) {
      console.error('MCP server error:', error);
      throw new Error(`Failed to process MCP request: ${error.message}`);
    }
  }
 
  public async sendMessage(message: string): Promise<string> {
    // Send the user message to Gemini
    const result = await this.chat.sendMessage(message);
    const responseText = result.response.text();
    
    // Check if the response contains an MCP request
    try {
      // Try to parse as JSON
      if (responseText.includes('{') && responseText.includes('}')) {
        const jsonStart = responseText.indexOf('{');
        const jsonEnd = responseText.lastIndexOf('}') + 1;
        const jsonStr = responseText.substring(jsonStart, jsonEnd);
        const mcpRequest = JSON.parse(jsonStr) as MCPRequest;
        
        if (mcpRequest.action && mcpRequest.parameters) {
          // This is an MCP request
          console.log('Detected MCP request:', mcpRequest);
          
          // Process the MCP request
          const mcpResponse = await this.processMCPRequest(mcpRequest);
          
          // Send the response back to Gemini
          const formattedResponse = JSON.stringify(mcpResponse.content, null, 2);
          const followUpResult = await this.chat.sendMessage(
            `Here are the results of your request:\n\`\`\`json\n${formattedResponse}\n\`\`\`\nPlease continue based on this information.`
          );
          
          return followUpResult.response.text();
        }
      }
    } catch (error) {
      console.error('Error processing potential MCP request:', error);
    }
    
    // If no MCP request was detected or processing failed, return the original response
    return responseText;
  }
}
 
export { GeminiMCPClient };

Advanced MCP Implementation Techniques

Stateful Operations

Implementing stateful operations requires session management in the MCP server:

// sessionManager.js
const sessions = new Map();
 
function getSession(sessionId) {
  if (!sessions.has(sessionId)) {
    sessions.set(sessionId, {
      id: sessionId,
      created: new Date().toISOString(),
      data: {},
      history: []
    });
  }
  return sessions.get(sessionId);
}
 
function updateSession(sessionId, data) {
  const session = getSession(sessionId);
  session.data = { ...session.data, ...data };
  session.updated = new Date().toISOString();
  return session;
}
 
function addToHistory(sessionId, request, response) {
  const session = getSession(sessionId);
  session.history.push({
    timestamp: new Date().toISOString(),
    request,
    response
  });
  
  // Limit history size
  if (session.history.length > 100) {
    session.history = session.history.slice(-100);
  }
  
  return session;
}
 
module.exports = {
  getSession,
  updateSession,
  addToHistory
};

Multi-function Operation

Configure Gemini 2.5 Pro to handle multiple MCP function types:

const functionRegistry = {
  'search': require('./functions/search'),
  'weather': require('./functions/weather'),
  'calendar': require('./functions/calendar'),
  'email': require('./functions/email'),
  'database': require('./functions/database')
};
 
async function handleRequest(mcpRequest, logger) {
  const { action, parameters } = mcpRequest.content;
  
  if (!functionRegistry[action]) {
    throw new Error(`Unsupported action: ${action}`);
  }
  
  logger.info(`Processing action: ${action} with parameters: ${JSON.stringify(parameters)}`);
  
  const result = await functionRegistry[action](parameters);
  
  return {
    message_id: `resp_${Date.now()}`,
    timestamp: new Date().toISOString(),
    source: 'server',
    destination: 'client',
    message_type: 'response',
    content: result
  };
}

Performance Optimization

Optimizing MCP server performance for Gemini 2.5 Pro requires attention to several technical aspects:

Request Batching

Implement request batching for efficient processing:

// batchProcessor.js
class BatchProcessor {
  constructor(options = {}) {
    this.maxBatchSize = options.maxBatchSize || 10;
    this.maxWaitTime = options.maxWaitTime || 50; // ms
    this.queue = [];
    this.timer = null;
  }
  
  add(request) {
    return new Promise((resolve, reject) => {
      this.queue.push({
        request,
        resolve,
        reject
      });
      
      if (this.queue.length >= this.maxBatchSize) {
        this.processQueue();
      } else if (!this.timer) {
        this.timer = setTimeout(() => this.processQueue(), this.maxWaitTime);
      }
    });
  }
  
  async processQueue() {
    clearTimeout(this.timer);
    this.timer = null;
    
    if (this.queue.length === 0) return;
    
    const batch = this.queue.splice(0, this.maxBatchSize);
    const requests = batch.map(item => item.request);
    
    try {
      const results = await this.processBatch(requests);
      batch.forEach((item, index) => {
        item.resolve(results[index]);
      });
    } catch (error) {
      batch.forEach(item => {
        item.reject(error);
      });
    }
  }
  
  async processBatch(requests) {
    // Implement batch processing logic
    throw new Error('processBatch method must be implemented by subclass');
  }
}

Caching Mechanism

Implement intelligent caching to reduce redundant API calls:

// cacheManager.js
const NodeCache = require('node-cache');
 
class CacheManager {
  constructor(options = {}) {
    this.cache = new NodeCache({
      stdTTL: options.ttl || 300, // 5 minutes default
      checkperiod: options.checkPeriod || 60
    });
  }
  
  generateKey(action, parameters) {
    return `${action}:${JSON.stringify(parameters)}`;
  }
  
  async getOrFetch(action, parameters, fetchFunction) {
    const cacheKey = this.generateKey(action, parameters);
    
    // Try to get from cache
    const cachedResult = this.cache.get(cacheKey);
    if (cachedResult) {
      return {
        ...cachedResult,
        _cache: { hit: true, key: cacheKey }
      };
    }
    
    // If not in cache, fetch fresh data
    const result = await fetchFunction();
    
    // Store in cache
    this.cache.set(cacheKey, result);
    
    return {
      ...result,
      _cache: { hit: false, key: cacheKey }
    };
  }
  
  invalidate(action, parameters) {
    const cacheKey = this.generateKey(action, parameters);
    this.cache.del(cacheKey);
  }
  
  flush() {
    this.cache.flushAll();
  }
}

Security Considerations

Implementing secure MCP servers with Gemini 2.5 Pro requires robust security measures:

Request Validation

Implement thorough request validation using JSON Schema:

// validators/requestValidator.js
const Ajv = require('ajv');
const ajv = new Ajv();
 
const mcpRequestSchema = {
  type: 'object',
  required: ['message_id', 'timestamp', 'source', 'destination', 'message_type', 'content'],
  properties: {
    message_id: { type: 'string' },
    timestamp: { type: 'string', format: 'date-time' },
    source: { type: 'string' },
    destination: { type: 'string' },
    message_type: { type: 'string', enum: ['request', 'response', 'error'] },
    content: {
      type: 'object',
      required: ['action', 'parameters'],
      properties: {
        action: { type: 'string' },
        parameters: { type: 'object' }
      }
    },
    metadata: { type: 'object' }
  }
};
 
const validateMCPRequest = ajv.compile(mcpRequestSchema);
 
function validateRequest(request) {
  const valid = validateMCPRequest(request);
  if (!valid) {
    const errors = validateMCPRequest.errors;
    throw new Error(`Invalid MCP request: ${JSON.stringify(errors)}`);
  }
  return true;
}
 
module.exports = { validateRequest };

Rate Limiting

Implement rate limiting to prevent abuse:

// middleware/rateLimiter.js
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
 
const redisClient = new Redis({
  host: process.env.REDIS_HOST || 'localhost',
  port: process.env.REDIS_PORT || 6379,
  password: process.env.REDIS_PASSWORD
});
 
const limiter = rateLimit({
  store: new RedisStore({
    sendCommand: (...args) => redisClient.call(...args)
  }),
  windowMs: 60 * 1000, // 1 minute
  max: 60, // limit each IP to 60 requests per minute
  standardHeaders: true,
  legacyHeaders: false,
  keyGenerator: (req) => {
    // Use API key or IP address as rate limit key
    return req.headers['x-api-key'] || req.ip;
  },
  handler: (req, res) => {
    res.status(429).json({
      message_id: `error_${Date.now()}`,
      timestamp: new Date().toISOString(),
      message_type: 'error',
      content: {
        error: 'Too many requests, please try again later'
      }
    });
  }
});

Conclusion

Gemini 2.5 Pro's implementation of the MCP protocol represents a significant advancement in AI system architecture, enabling seamless integration between the language model and external services. By following the technical implementation guidelines outlined in this article, developers can create sophisticated AI systems capable of complex automation workflows and real-time data access.

The continued evolution of MCP servers with Gemini 2.5 Pro will likely see enhanced capabilities in multi-modal processing, more sophisticated state management, and expanded service integration options. By mastering these implementation techniques, developers can position themselves at the forefront of AI system architecture and create increasingly powerful automation solutions.