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:
- Request Handler: Processes incoming requests from Gemini 2.5 Pro, validates message format, and routes to appropriate service adapters
- Service Adapters: Translates standardized MCP protocol messages into service-specific API calls
- Response Formatter: Converts external API responses into standardized MCP message format
- Session Manager: Maintains stateful interactions across multiple requests
- Authentication Module: Manages OAuth flows and API key authentication
Client Implementation
The client side, implemented within Gemini 2.5 Pro's context, requires:
- MCP Protocol Parser: Interprets and generates protocol-compliant messages
- Request Formatter: Constructs well-formed MCP request objects
- Response Handler: Processes MCP response messages and extracts relevant data
- 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:
- Initialize the project structure:
mkdir gemini-mcp-server
cd gemini-mcp-server
npm init -y
npm install express cors winston axios dotenv
- 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
- 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}`);
});
- 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.